> ## Documentation Index
> Fetch the complete documentation index at: https://mediadrop.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# useMediaDrop

> Full API reference for the useMediaDrop hook

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { useMediaDrop } from "react-mediadrop";
```

Headless hook. No prebuilt component — you own the markup.

## Options

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
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](/guides/core-concepts#how-do-i-know-when-a-file-is-being-dragged). |
| `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](/guides/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.

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
<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:

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
<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: 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:

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
<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>` 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](/guides/core-concepts#can-i-use-multiple-dropzones-on-one-page).
* There's no dashboard/progress UI to import — every list item, remove
  button, progress bar, and status message is yours to build.

<CardGroup cols={2}>
  <Card title="Core concepts" icon="cube" href="/guides/core-concepts">
    The file model, the store, and drag state
  </Card>

  <Card title="Types" icon="brackets-curly" href="/api-reference/types">
    Every shared type exported from react-mediadrop
  </Card>
</CardGroup>
