Skip to content

File dropzone

import { FileDropzone } from '@yarcl/react';
<FileDropzone
label="Attachments"
description="Images or PDF files"
accept="image/*,.pdf"
multiple
onFilesChange={(files) => upload(files)}
/>

The button opens the native file picker. Files can also be dropped on the bordered area. accept filters dropped files as well as picker selections. multiple shows a file list with remove buttons. Use the render prop to provide your own list:

<FileDropzone label="Attachments" multiple>
{(files, removeFile) => (
<ul>
{files.map((file, index) => (
<li key={`${file.name}-${index}`}>
{file.name}
<button type="button" onClick={() => removeFile(index)}>Remove</button>
</li>
))}
</ul>
)}
</FileDropzone>

Use onFilesChange to upload or validate the selected files. Upload progress belongs to the upload request, so render it beside this component.

Without multiple, the dropzone shows one current file with Replace and Remove controls. Pass preview to show a thumbnail for a selected image. Use value with onChange for a controlled picker, or defaultValue for an uncontrolled initial file.

const [cover, setCover] = useState<File | null>(null);
<FileDropzone
label="Course cover"
accept="image/*"
value={cover}
onChange={setCover}
preview
/>

For an existing image hosted by your app, pass previewUrl and previewName. Clear the URL in your onChange handler when the user replaces or removes it:

<FileDropzone
label="Course cover"
accept="image/*"
value={cover}
onChange={(file) => { setCover(file); setCoverUrl(undefined); }}
preview
previewUrl={coverUrl}
previewName="Current cover"
/>

onFilesChange still receives an array in either mode for existing upload handlers. In single-file mode, onChange receives the file or null after removal.

With multiple, use files and onFilesChange to own the selected list, or defaultFiles for an uncontrolled initial list. Picker selections and drops replace the list after applying accept; removing a file proposes a new list. A controlled picker displays the parent’s files until the parent updates them. Pass [] to clear it after an upload. Arrays can be readonly and are never mutated.

import { useState } from 'react';
import { FileDropzone } from '@yarcl/react';
function Attachments() {
const [files, setFiles] = useState<readonly File[]>([]);
return (
<FileDropzone
label="Attachments"
multiple
files={files}
onFilesChange={setFiles}
/>
);
}

For upload-on-select, leave files={[]} and start the request in onFilesChange. Display progress and uploaded attachments in your own list. The native input resets after every selection, allowing the same file to be picked again. Cancelling the picker or selecting only rejected files leaves the list unchanged.

files and defaultFiles apply only to multi-file mode. value, defaultValue, onChange, and preview props apply only to single-file mode. Choose the API that matches multiple.

inputRef exposes the existing native file input. Call its click() from a user action, such as a Replace button. This opens the same picker with the same accept, multiple, disabled state and callbacks. The regular ref still targets the outer div. Object refs and React 19 callback refs with cleanup are supported.

import { useRef } from 'react';
import { Button, FileDropzone } from '@yarcl/react';
function AttachmentPicker() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<Button onClick={() => inputRef.current?.click()}>Replace attachment</Button>
<FileDropzone
label="Attachments"
inputRef={inputRef}
onFilesChange={(files) => upload(files)}
/>
</>
);
}

Browsers require a user action to open a file picker. Disable an external Replace control when the dropzone is disabled. Set the attachment being replaced in your own state before opening the picker; the component handles file selection, while your app decides which upload to replace.

Use labels to override picker and drop instructions locally, or translate config.labels for the whole app:

<FileDropzone
label="Attachments"
multiple
labels={{
chooseFiles: 'Browse attachments',
dropFiles: 'Drop attachments here',
removeItem: (name) => `Delete ${name}`,
}}
/>

The visible label names the group. The picker button works with Enter and Space; the hidden input stays out of the tab sequence. Selected files are announced through a polite live region, and Remove buttons include each file’s name. Disabled controls ignore picking and dropping. Keep uploaded-file actions outside the dropzone’s internal list when they belong to a separate server-side attachment state.

PropTypeDefaultDescription
label requiredstringVisible label for the picker and drop area.
acceptstringAccepted extensions or MIME types, as on a native file input.
children(files: readonly File[], removeFile: (index: number) => void) => ReactNodeCustom file list renderer. Receives the files and a remove function.
defaultFilesreadonly File[]Initial files in uncontrolled multi-file mode.
defaultValueFile | nullInitial file in uncontrolled single-file mode.
descriptionstringOptional helper text below the label.
disabledbooleanDisables picking and dropping files.
filesreadonly File[]Selected files in multi-file mode. Pass an empty array to reset after uploading.
inputRefRef<HTMLInputElement>Native picker input. Call inputRef.current?.click() from an external button.
labelsPartial<FileDropzoneLabels>Overrides the active catalog for this picker. Unspecified entries keep config labels.
multiplebooleanAllows more than one file.
onChange(file: File | null) => voidCalled with the selected file or null when it is removed in single-file mode.
onFilesChange(files: File[]) => voidCalled whenever the selected file list changes.
previewbooleanShows a thumbnail when the selected file is an image.
previewNamestringName shown for a remote file supplied through previewUrl.
previewUrlstringImage URL for an existing remote file in single-file mode.
valueFile | nullSelected file in single-file mode. Use null for no file.