> ## 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.

# Validation

> Restrictions and custom validators

`useMediaDrop` rejects a file the moment it's added if it fails
`restrictions` or your own `validator` — both take the same shape on
every call.

## What can restrictions check?

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type MediaDropRestrictions = {
	maxFiles?: number;
	minSize?: number; // bytes
	maxSize?: number; // bytes
	accept?: string[] | string; // mime types, wildcards, or extensions
};
```

`accept` tokens can be:

* An exact mime type: `"image/png"`
* A wildcard mime type: `"image/*"`
* A file extension: `".png"` (matched against the file name,
  case-insensitive)

You can pass an array or a comma-separated string (`"image/png,image/webp"`
is equivalent to `["image/png", "image/webp"]`).

An empty/missing `accept` accepts every file type.

## What does a validation error look like?

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type MediaDropErrorCode =
	| "file-invalid-type"
	| "file-too-large"
	| "file-too-small"
	| "too-many-files"
	| "validator-error"
	| "upload-error";

type MediaDropError = {
	code: MediaDropErrorCode;
	message: string; // for display — don't branch on this
	status?: number; // HTTP status, upload errors only
	sourceCode?: string; // transport-specific finer-grained code, upload errors only
};
```

Every rejected `MediaDropFile` has a non-empty `errors` array using these
codes (plus whatever code your custom validator assigns). Switch on
`code`, not on `message` — messages are for display, not branching logic.
`status`/`sourceCode` are only ever populated on upload errors (see
[Upload](/guides/upload)) — always absent on a validation error.

## How do I write a custom validator?

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type MediaDropValidator = (
	file: File,
) => MediaDropError | MediaDropError[] | null | undefined;
```

Return `null`/`undefined` to pass. Return one error or an array of errors
to reject the file — you choose the `code` (use `"validator-error"`
unless you have a better fit from the built-in codes) and the `message`.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
function validator(file: File) {
	if (file.name.includes(" ")) {
		return { code: "validator-error", message: "Filenames can't contain spaces" };
	}
	return null;
}
```

Do not reimplement `accept`/`maxSize`/`minSize` checks inside a custom
validator — use `restrictions` for those. Reserve the validator for rules
`restrictions` can't express (content sniffing, naming conventions,
cross-file business rules, etc.).

## Does the validator run during drag?

The validator's primary job is drop-time validation, but `useMediaDrop`
also runs it as part of the best-effort `isDragAccept`/`isDragReject`
preview during an active drag — see [Core concepts](/guides/core-concepts#how-do-i-know-when-a-file-is-being-dragged).
This only happens when the browser hands back a real `File` via
`DataTransferItem.getAsFile()` before drop; when it doesn't, the preview
silently falls back to accept-only evaluation.

Don't rely on this for correctness — the authoritative accept/reject
decision is always the one made at drop time.

## What doesn't validation do?

* **No async validators.** The validator runs synchronously against the
  `File` object (name/size/type only) — it cannot read file contents or
  await a network check.
* **No re-validation after the fact.** Once a file is added, its `status`
  and `errors` don't change unless you remove and re-add it.
* **No image-specific checks** (dimensions, aspect ratio, decode-ability).
  If you need that, it's a custom validator today, not a first-class
  option.

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

  <Card title="Upload" icon="cloud-arrow-up" href="/guides/upload">
    The queue, concurrency, retry, and cancel
  </Card>
</CardGroup>
