InputsReact
A drop zone and the list of the files it holds.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<FileUpload.Root>
<FileUpload.Dropzone />
<FileUpload.List>
<FileUpload.Item>
<FileUpload.ItemName />
<FileUpload.ItemSize />
<FileUpload.ItemRemove />
<FileUpload.ItemError />
</FileUpload.Item>
</FileUpload.List>
<FileUpload.Clear />
</FileUpload.Root>
// Not in this example: FileUpload.ItemPreview, FileUpload.TriggerExamples
Images
accept="image/*", maxFiles={4} and maxSize of 1 MB: a "Choose images" Trigger inside the drop zone, previews in a grid, and the files past the limits listed with the reason.
Form
A required PDF in a Field of a Form: Send without a file shows "Attach your résumé before you send.", and with one the form submits resume= and the file's name.
Compact
A Trigger alone, without multiple: "Upload avatar" picks one image, a new pick replaces it, and the file shows as a chip with its size.
API reference
36 props across 11 parts, in React syntax. className, style, render are listed once, under Shared props.
ClearFileUpload.Clear
Removes every file and the rejections. Focus stays on it after a press. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
focusableWhenDisabledWhether the button stays focusable while there is nothing to clear, so focus stays on it | boolean | true |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when there are no files and no rejections to clear, or the file upload is disabled or read-only. |
DropzoneFileUpload.Dropzone
The area files are dropped on. A button that opens the picker, named by its content and the Field label, unless it contains a Trigger or openOnPress is false: it is then a drop and paste target only. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
openOnPressWhether a press opens the picker, which makes the Dropzone a button. | boolean | true |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
| Data attribute | Description |
|---|---|
data-dragging | Present while files are dragged over the Dropzone. |
data-drag-accept | Present while the dragged files would all be accepted. |
data-drag-reject | Present while the dragged files would be rejected. |
data-disabled | Present when the file upload is disabled. |
data-readonly | Present when the file upload is read-only. |
data-valid | Present when the file upload is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the file upload is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the file upload has been touched (when wrapped in Field.Root). |
data-dirty | Present when the files have changed (when wrapped in Field.Root). |
data-filled | Present when the file upload holds a file (when wrapped in Field.Root). |
data-focused | Present when a control of the file upload is focused (when wrapped in Field.Root). |
ItemFileUpload.Item
One file of a list. Renders a <li>.
| Prop | Type | Default |
|---|---|---|
filerequiredThe file the item shows. | File | — |
statusAny status the application sets, such as uploading, error or done, as data-status. | string | — |
| Data attribute | Description |
|---|---|
data-rejected | Present when the item is in the list of rejections. |
data-status | The status the application set. |
data-disabled | Present when the file upload is disabled. |
data-readonly | Present when the file upload is read-only. |
ItemErrorFileUpload.ItemError
Why a rejected file was not added: its messages, which also describe the item's remove button. Renders nothing for a file without errors. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
childrenThe text, or a function of the file's errors. | React.ReactNode | ((errors: readonly FileUploadError[]) => React.ReactNode) | — |
ItemNameFileUpload.ItemName
The file's name. Renders a <span>.
ItemPreviewFileUpload.ItemPreview
A thumbnail of an image file, from an object URL created after mount and revoked when the file changes or the part unmounts. Renders nothing for a file that is not an image, or whose image fails to load. Decorative by default. Renders an <img>.
| Prop | Type | Default |
|---|---|---|
altThe image's text alternative. | string | '' |
| Data attribute | Description |
|---|---|
data-status | Whether the image is loading, loaded, or failed to load. |
ItemRemoveFileUpload.ItemRemove
Removes the item's file, or dismisses its rejection. Named "Remove report.pdf" unless it has text of its own; focus moves to the next remove button after a press. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the file upload is disabled. |
data-readonly | Present when the file upload is read-only. |
ItemSizeFileUpload.ItemSize
The file's size, in decimal units formatted with locale ("1.23 MB"). Renders a <span>.
| Prop | Type | Default |
|---|---|---|
childrenThe text, or a function of the formatted size and the size in bytes. | React.ReactNode | ((formattedSize: string, bytes: number) => React.ReactNode) | — |
ListFileUpload.List
The files of the value, or with rejected the files of the last attempt that were not added. The element stays when the list is empty. Renders an <ul>.
| Prop | Type | Default |
|---|---|---|
rejectedWhether the list shows the files of the last attempt that were not added, instead of the | boolean | false |
childrenThe items, or a function of each file and its index that returns one, keyed by the file. | React.ReactNode | ((file: File, index: number) => React.ReactNode) | — |
| Data attribute | Description |
|---|---|
data-rejected | Present when the list shows the last attempt's rejections. |
data-empty | Present when the list has no files. |
RootFileUpload.Root
Groups the parts of a file upload and holds its files. Renders the visually hidden file input that is both the picker and the form value, and the status region that announces changes. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
valueThe files. | File[] | — |
defaultValueThe files when the component is initially rendered. | File[] | — |
onValueChangeEvent handler called when the files change. | ((value: File[], eventDetails: FileUploadRootChangeEventDetails) => void) | — |
onFileRejectEvent handler called when files of an attempt are not added, each with its errors. | ((rejections: FileUploadRejection[], eventDetails: FileUploadRootRejectEventDetails) => void) | — |
acceptThe file types accepted, as HTML's accept token string: MIME types, type/* wildcards | string | — |
multipleWhether the value holds several files that new files join. | boolean | false |
maxFilesWith multiple, the most files the value holds. | number | — |
maxSizeThe largest file accepted, in bytes. | number | — |
minSizeThe smallest file accepted, in bytes. | number | 0 |
validateChecks a file after the built-in checks pass. | FileUploadValidate | — |
directoryWhether the picker selects folders. | boolean | false |
captureThe camera a mobile picker opens, passed to the input. | "user" | "environment" | — |
allowDropWhether files dropped on a Dropzone are added. | boolean | true |
allowPasteWhether files pasted while focus is inside the component are added. | boolean | true |
preventDocumentDropWhether a file dropped outside every Dropzone and outside editable fields is prevented from | boolean | true |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the files. | boolean | false |
requiredWhether the user must add a file before submitting a form. | boolean | false |
labelsThe texts, in English by default. | Partial<FileUploadLabels> | — |
localeThe locale used by Intl.NumberFormat when formatting sizes and counts. | Intl.LocalesArgument | — |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the file input. | string | — |
inputRefA ref to access the file input element. | React.Ref<HTMLInputElement> | — |
| Data attribute | Description |
|---|---|
data-disabled | Present when the file upload is disabled. |
data-readonly | Present when the file upload is read-only. |
data-required | Present when the file upload is required. |
data-dragging | Present while files are dragged over one of the Dropzones. |
data-document-dragging | Present while files are dragged anywhere over the document. |
data-processing | Present while a dropped folder is read or validate is pending. |
data-max-reached | Present when multiple is set and the value holds maxFiles files. |
data-valid | Present when the file upload is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the file upload is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the file upload has been touched (when wrapped in Field.Root). |
data-dirty | Present when the files have changed (when wrapped in Field.Root). |
data-filled | Present when the file upload holds a file (when wrapped in Field.Root). |
data-focused | Present when a control of the file upload is focused (when wrapped in Field.Root). |
TriggerFileUpload.Trigger
Opens the file picker. Inside a Dropzone it is the one control, and the Dropzone stays a drop and paste target. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the file upload is disabled. |
data-readonly | Present when the file upload is read-only. |
data-valid | Present when the file upload is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the file upload is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the file upload has been touched (when wrapped in Field.Root). |
data-dirty | Present when the files have changed (when wrapped in Field.Root). |
data-filled | Present when the file upload holds a file (when wrapped in Field.Root). |
data-focused | Present when a control of the file upload is focused (when wrapped in Field.Root). |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| Tab | Moves to the Dropzone, one Tab stop; the hidden input is skipped | Dropzone |
| EnterSpace | Opens the file picker once: Enter on keydown, Space on keyup; focus stays on the Dropzone | Dropzone |
| TabTrigger | Moves to the Trigger, the one Tab stop; the Dropzone has no role and no Tab stop | Dropzone |
| EnterSpaceTrigger | Opens the file picker | Trigger |
| ControlVMetaV | Adds the pasted files while focus is inside the Root, with reason input-paste; a text paste is left alone | Root |
| EnterSpaceItemRemove | Removes its file; focus moves to the next remove button, then the previous one, then the Dropzone | ItemRemove |
| EnterSpaceClear | Empties the value and the rejections with reason clear-press; focus stays on Clear | Clear |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Dropzone | role="button", tabindex="0", named by its content and Field.Label, described by Field.Description; no aria-label | the drop zone is read as the button it is, with the text it shows; a development build warns without a name |
| Dropzone | no role and no Tab stop when a Trigger is inside it, or with openOnPress={false} | there is never a button inside a button |
| Trigger | a button with type="button", named by its content, labelled by Field.Label when it is the only control | the picker opens from a real button that never submits the form |
| Root | one input type="file" with aria-hidden="true" and tabindex="-1"; focus that reaches it moves to the Dropzone or Trigger | the files are submitted with the form while the input is never a stop of its own |
| List | a ul | the files are read as a list with a count |
| Item | an li for each file, in the order of the value | each file is one entry of the list |
| ItemRemove | a button with type="button", named "Remove {name}" or, on a rejection, "Dismiss {name}" (labels), or by its text | each button says which file it removes |
| ItemError | an id that is the aria-describedby of the item's ItemRemove | the reason a file was not added is read with the button that dismisses it |
| Clear | a button named "Remove all files" (labels.clear) or by its text; disabled but focusable while there is nothing to clear | clearing is reached without a press on each file |
| Root | one polite, atomic role="status" region, empty on mount, always mounted | "brief.pdf added", "2 files added", a removal and a rejection with its reason are announced |
Focus order
- The
Dropzoneis one Tab stop, or theTriggerwhen there is no buttonDropzone. Nothing takes focus on mount, and the hidden input is never a stop. - Every
ItemRemoveis a Tab stop after it, in the order of the files, thenClear. - After a removal focus moves to the next remove button, else the previous one, else the
Dropzone(or theTriggerwithout one). AfterClearfocus stays on it. - A read-only upload keeps its Tab stops with
aria-disabled; a disabled one has none. A required, empty upload in a Form gets focus on submit, on itsDropzone.