useMediaDrop
Full API reference for the useMediaDrop hook
import { useMediaDrop } from "react-mediadrop";
Headless hook. No prebuilt component — you own the markup.
Options
function useMediaDrop(options?: {
restrictions?: MediaDropRestrictions;
validator?: MediaDropValidator;
noClick?: boolean; // disable click-to-open on the root
noKeyboard?: boolean; // disable Space/Enter-to-open and focus tracking
noDrag?: boolean; // disable the root's drag/drop handling
transport?: UploadTransport; // opt into upload — see below
concurrency?: number; // max uploads in flight at once. Default 1.
retries?: number; // retries after the first attempt. Default 0.
retryDelays?: number[]; // backoff per retry.
cancelGraceMs?: number; // force-free a slot after cancel. Default 5000.
}): UseMediaDropResult;
Return value
| Field | Type | Notes |
|---|---|---|
files / acceptedFiles / rejectedFiles |
MediaDropFile[] |
Every file added, filtered by status. |
isDragActive / isDragAccept / isDragReject |
boolean |
Per-dropzone drag state — best-effort, see Core concepts. |
isFocused |
boolean |
Root element has keyboard focus. Always false when noKeyboard is set. |
isDragGlobal |
boolean |
A file drag is happening anywhere on the document, not just this root. |
removeFile(id) / clearFiles() |
() => void |
Mutate the file list. Cancels any in-flight upload for removed files. |
open() |
() => void |
Imperatively open the native file picker. |
getRootProps(arg?) |
() => RootProps |
Drag/drop + click/keyboard handlers, role, tabIndex. |
getInputProps(arg?) |
() => InputProps |
Hidden <input type="file"> props. |
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.
<div
{...getRootProps({
"aria-label": "File upload dropzone",
className: "dropzone",
})}
>
<input {...getInputProps({ "aria-hidden": "true" })} />
</div>
They compose with your own handlers — yours always runs first:
<div {...getRootProps({ onDrop: (e) => console.log("also dropped", e) })}>
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: truedisables click-to-open (keepopen()for a manual “Choose files” button instead).noKeyboard: truedisables Space/Enter-to-open, removestabIndexfrom the returned props, and stops trackingisFocused.noDrag: truedisables 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:
<div {...getRootProps()}>
<input {...getInputProps()} />
<button
type="button"
onClick={(event) => {
event.stopPropagation();
open();
}}
>
Choose files
</button>
</div>
Things to get right
- Don’t wrap the returned
<input>indisplay: noneyourself and expectopen()to fail —getInputProps()already hides it andopen()calls.click()on it programmatically, which works throughdisplay: nonein all evergreen browsers. - The hook never touches
window/documentduring render, so it’s SSR-safe.isDragGlobal’sdocumentlisteners are registered inside auseEffect(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. Changingrestrictions/validatorprops after mount changes future drag-acceptance previews but does not retroactively re-validate files already infiles. - 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.