Multi-file drag-and-drop upload area built on react-dropzone. Files are uploaded immediately on drop/selection; Dropzone.List renders the uploaded files with progress and remove controls.
Use Dropzone for uploading one or more files via drag-and-drop or a click-to-browse area. Pair the drop area (Dropzone) with Dropzone.List — both share the same value (a DropzoneFile[]) and onChange so the list stays in sync with what was dropped.
@uxf/form form: use @uxf/form/dropzone instead — it wires react-hook-form (useController), reads the form context, and adds count validation (minFilesCount / maxFilesCount). @uxf/ui/dropzone is the controlled primitive you drive with value / onChange.FileInput.AvatarFileInput.import { FileResponse } from "@uxf/core/types";
import { Dropzone } from "@uxf/ui/dropzone";
import { DropzoneFile } from "@uxf/ui/dropzone/types";
import { getDropzoneState } from "@uxf/ui/utils/get-dropzone-state";
import { useState } from "react";
// your uploader: send the file to storage, resolve with the stored file
declare function uploadFile(file: File): Promise<FileResponse>;
function Example() {
const [files, setFiles] = useState<DropzoneFile[]>([]);
const { status } = getDropzoneState(files);
return (
<>
<Dropzone
isDisabled={status === "UPLOADING"}
label="Use drag and drop or click to upload"
name="attachments"
onChange={setFiles}
onUploadFile={uploadFile}
value={files}
/>
<Dropzone.List name="attachments" onChange={setFiles} value={files} />
</>
);
}
Import from @uxf/ui/dropzone:
| Export | Kind | Description |
|---|---|---|
Dropzone |
component | The drop area (DropzoneInput). |
Dropzone.List |
component | List of uploaded files (DropzoneList). |
DropzoneInputProps |
type | Props of Dropzone. |
DropzoneListProps |
type | Props of Dropzone.List. |
Related helpers and types (separate deep imports, not on the component):
getDropzoneState — @uxf/ui/utils/get-dropzone-stateDropzoneFile type — @uxf/ui/dropzone/typesDropzone props (DropzoneInputProps)| Prop | Type | Default | Description |
|---|---|---|---|
value |
DropzoneFile[] | undefined |
— | Required. Current files (controlled). |
onChange |
(value: DropzoneFile[] | undefined, event?) => void |
— | Required. Receives the updated file list. |
onUploadFile |
(file: File, options?: UploadOptions) => Promise<FileResponse> |
— | Required. Uploads a dropped file; called per file with an AbortController and progress callback. |
name |
string |
— | Required. Field name (also emitted as data-name). |
accept |
Accept ({ [mime: string]: string[] }) |
— | Accepted MIME types in react-dropzone format, e.g. { "image/*": [] }. |
maxFileSize |
number |
— | Max size per file in bytes. |
minFileSize |
number |
— | Min size per file in bytes. |
maxFilesCount |
number |
— | Max number of files. When 1, the input is single-file (multiple is off). |
icon |
IconName |
"cloud" |
Icon shown in the drop area. |
label |
ReactNode |
— | Drop area label. |
helperText |
ReactNode |
— | Helper / error text under the drop area. |
isNotClickable |
boolean |
false |
Disable click-to-browse (drag only). |
isNotDraggable |
boolean |
false |
Disable dragging (click only). |
onDropRejected |
(fileRejections: FileRejection[]) => void |
— | Called for files rejected by accept/size/count (react-dropzone FileRejection[]). |
onUploadComplete |
(files: FileResponse[]) => Promise<void> |
— | Called once all uploads settle, with the successfully uploaded files. |
onUploadError |
(err: unknown) => void |
— | Called when an individual upload fails. |
isDisabled |
boolean |
false |
Disable the drop area. |
isReadOnly / isInvalid / isRequired / isFocused |
boolean |
false |
State flags (styling / data-*). |
id |
string |
— | Input id. |
className / style |
string / CSSProperties |
— | Root styling. |
Dropzone.List props (DropzoneListProps)| Prop | Type | Default | Description |
|---|---|---|---|
value |
DropzoneFile[] | undefined |
— | Required. Files to render (share with Dropzone). |
onChange |
(value: DropzoneFile[] | undefined, event?) => void |
— | Required. Receives the list after a removal. |
name |
string |
— | Required. Field name. |
errorText |
string |
"File upload error" |
Text shown under a file that failed to upload. |
isDownloadableOnClick |
boolean |
false |
Render the file link with download instead of opening in a new tab. |
onRemoveConfirm |
(file: DropzoneFile) => Promise<boolean> |
— | Confirm before removing; remove only if it resolves true. |
renderItem |
(file, onRemove, isUploading) => ReactNode |
— | Custom renderer for each list item (replaces the default row). |
isDisabled |
boolean |
false |
Hide the per-item remove buttons. |
className / style |
string / CSSProperties |
— | List styling. |
getDropzoneStateimport { getDropzoneState } from "@uxf/ui/utils/get-dropzone-state";
const { status, errorMessage } = getDropzoneState(files);
// status: "OK" | "UPLOADING" | "ERROR"
Derives the overall upload state from the file list. Use status === "UPLOADING" to disable the drop area while files are in flight.
DropzoneFileExtends FileResponse (@uxf/core/types) with upload bookkeeping:
| Field | Type | Description |
|---|---|---|
originalFile |
File |
The picked file (present until the server response replaces it). |
progress |
number | null |
Upload progress 0–100, or null when not computable. |
error |
unknown |
Set when the file's upload failed. |
abortController |
AbortController |
Aborts the in-flight upload when the file is removed. |
id and a progress value until its onUploadFile promise resolves and the server FileResponse replaces it.abortController.abort().onDropRejected fires when react-dropzone rejects files (wrong type, too large/small, too many). A ready-made handler exists at @uxf/ui/dropzone/handle-rejected-files (handleRejectedFiles), but it shows hardcoded Czech alert() messages — prefer your own handler in production.UiContext that provides domain, already-uploaded file names in Dropzone.List render as links to the stored file.Client component ("use client").
Import the required stylesheets once in your global CSS:
@import url("@uxf/ui/css/icon.css");
@import url("@uxf/ui/css/dropzone.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.