Back to all questions

Why FileButton does not work in Menu?

Last updated

Example of the issue

In the following example, the onChange callback will never be called even though the FileButton is rendered inside the Menu component and the file dialog is opened when the second Menu.Item is clicked.

import { Button, FileButton, Menu } from '@mantine/core';

function Demo() {
  return (
    <Menu>
      <Menu.Target>
        <Button>Toggle menu</Button>
      </Menu.Target>
      <Menu.Dropdown>
        <Menu.Item>Fist item</Menu.Item>
        <FileButton onChange={(file) => console.log(file)} accept="image/png,image/jpeg">
          {(props) => <Menu.Item {...props}>Upload Image</Menu.Item>}
        </FileButton>
      </Menu.Dropdown>
    </Menu>
  );
}

Source of the issue

The onChange callback is not triggered in the example above because, by default, Menu component is closed automatically when an item is clicked. When the Menu is closed, the FileButton is unmounted from the DOM with the underlying input[type="file"] element. When the file is selected, the input[type="file"] element is not in the DOM and the onChange callback is not triggered.

How to fix the issue

There are two solutions to this issue. The first one is to set keepMounted prop on the Menu component. This way, the FileButton will not be unmounted when the Menu is closed:

import { Button, FileButton, Menu } from '@mantine/core';

function Demo() {
  return (
    <Menu keepMounted>
      <Menu.Target>
        <Button>Toggle menu</Button>
      </Menu.Target>
      <Menu.Dropdown>
        <Menu.Item>Fist item</Menu.Item>
        <FileButton onChange={(file) => console.log(file)} accept="image/png,image/jpeg">
          {(props) => <Menu.Item {...props}>Upload Image</Menu.Item>}
        </FileButton>
      </Menu.Dropdown>
    </Menu>
  );
}

The other solution is to prevent Menu from closing when Menu.Item that contains FileButton is clicked. This can be done by setting closeOnItemClick={false} prop on the Menu.Item component:

import { Button, FileButton, Menu } from '@mantine/core';

function Demo() {
  return (
    <Menu>
      <Menu.Target>
        <Button>Toggle menu</Button>
      </Menu.Target>
      <Menu.Dropdown>
        <Menu.Item>Fist item</Menu.Item>
        <FileButton onChange={(file) => console.log(file)} accept="image/png,image/jpeg">
          {(props) => (
            <Menu.Item {...props} closeMenuOnClick={false}>
              Upload Image
            </Menu.Item>
          )}
        </FileButton>
      </Menu.Dropdown>
    </Menu>
  );
}