Skip to main content
Headless hook. No prebuilt component — you own the markup.

Options

Return value

Passing transport additionally returns uploadFile/uploadAll/ cancelUpload/cancelAllUploads/retryUpload — see Upload. Without transport, none of it exists on the returned object, and TypeScript won’t let you call it.

getRootProps/getInputProps

Both accept and pass through arbitrary HTML attributes alongside the recognized handlers — useful for aria-label/aria-describedby/ className/id/data-*. A consumer-supplied style on getInputProps() is merged, but display: none always wins — it can’t be overridden, since the hidden native input is load-bearing for click-to-open.
They compose with your own handlers — yours always runs first:
If your handler calls event.stopPropagation(), the hook’s own handling for that event is skipped. This is the one supported way to override built-in behavior.

Click-to-open and keyboard activation

By default, getRootProps() makes the root element click- and keyboard-activatable: clicking anywhere in the root, or pressing Space/Enter while it’s focused, opens the native file picker — the same thing open() does programmatically.
  • noClick: true disables click-to-open (keep open() for a manual “Choose files” button instead).
  • noKeyboard: true disables Space/Enter-to-open, removes tabIndex from the returned props, and stops tracking isFocused.
  • noDrag: true disables drag/drop handling on the root entirely — the input and click-to-open still work.
If you render your own “Choose files” button inside the root element, stop its click from bubbling to the root, or click-to-open fires a second time:

Things to get right

  • Don’t wrap the returned <input> in display: none yourself and expect open() to fail — getInputProps() already hides it and open() calls .click() on it programmatically, which works through display: none in all evergreen browsers.
  • The hook never touches window/document during render, so it’s SSR-safe. isDragGlobal’s document listeners are registered inside a useEffect (client-only) and removed on unmount. You can render it on the server without guards.
  • The engine backing one useMediaDrop() call is created once for that component instance’s lifetime. Changing restrictions/validator props after mount changes future drag-acceptance previews but does not retroactively re-validate files already in files.
  • Multiple useMediaDrop() calls on the same page are independent and safe — see Core concepts.
  • There’s no dashboard/progress UI to import — every list item, remove button, progress bar, and status message is yours to build.

Core concepts

The file model, the store, and drag state

Types

Every shared type exported from react-mediadrop