Pick or drop files, watch them upload with progress, retry failures, remove or cancel. The drop target is a real button, so the keyboard path is the same as the pointer path; type, size and count rejections come back as localized messages. Without an upload handler the files simply go with the form.
ID copy, signed contract and diploma. Name a file “fail.pdf” to see an error.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Field, FileUpload } from "@vamos-ui/react";
<Field label="Documents">
<FileUpload
accept=".pdf,image/*"
maxSize={10_000_000}
maxFiles={6}
upload={async (file, { signal, onProgress }) => {
const { id, url } = await uploadToStorage(file, { signal, onProgress });
return { id, url };
}}
/>
</Field>File names are NFC-normalised: macOS sends decomposed characters, which breaks the display of ş, ğ and é.
Resumable / chunked uploads (tus) are out of scope — wire them inside your upload function.
Examples
Image upload
One image with a live preview in a fixed aspect ratio; Replace and Remove appear on hover or keyboard focus (always on touch).
1600 × 900 or larger.
Restrictions
accept, maxSize and maxFiles reject with a reason and a localized message; sizes use the locale (2,4 MB in Turkish).
Drop a PDF, a 3 MB photo or more than three files to see each rejection.
Existing files (compact)
Server files are UploadFile objects without file. The compact variant sits in a form row like an input.
- Master services agreement — Moda Wellness.pdf1.8 MBUploaded
- Rate card 2026.xlsx96.4 kBUploaded
2 files · 0 new
Plain form submit
No upload: the hidden file input carries the files, so FormData and Server Actions work as is.
API
FileUpload
| Prop | Type | Default |
|---|---|---|
acceptMIME types, type/* wildcards and extensions: "image/*,.pdf". | string | string[] | — |
maxSize / minSizeBytes per file. | number | — |
maxFilesCounted over the list (failed files don't count). maxFiles={1} replaces the file. | number | — |
multipleAllow several files per pick. | boolean | true unless maxFiles is 1 |
value / defaultValue / onValueChange{ key, file?, name, size, type, status, progress?, error?, remoteId?, url?, previewUrl? }. | UploadFile[] | — |
uploadUploads each file when added; cancel aborts the signal; a rejected promise shows its message with Retry. | (file, { signal, onProgress }) => Promise<{ id, url? }> | — |
concurrencyParallel uploads. | number | 3 |
onRejectreason: type | size | small | count. | (rejections: { file, reason, message }[]) => void | — |
variantLook of the trigger. | "dropzone" | "button" | "compact" | "dropzone" |
hintReplaces the generated hint ("PDF or PNG · up to 10 MB"). | ReactNode | — |
showListRender the file list (or render your own with FileUploadList/Item). | boolean | true |
name · required · disabled · invalidForm name for the native input (only without upload); Field wiring. | — | — |
labelsBuilt-in strings (upload namespace). | Partial<FileUploadLabels> | — |
ImageUpload
| Prop | Type | Default |
|---|---|---|
value / defaultValue / onValueChangeThe single image. | UploadFile | null | — |
aspectCSS aspect ratio of the frame. | string | "16/9" |
fitcontain for logos. | "cover" | "contain" | "cover" |
accept / maxSize / upload / onReject / hintAs FileUpload. | — | — |
FileUploadList · FileUploadItem
| Prop | Type | Default |
|---|---|---|
fileThumbnail or type icon, name, size, status, per-file progressbar. | UploadFile | — |
onRemove / onRetryRemove (or cancel while uploading) and retry buttons, named after the file. | () => void | — |
useFileDropzone
| Prop | Type | Default |
|---|---|---|
useFileDropzone(options)The drag-and-drop + picker wiring with localized rejections, for custom drop targets. | { rootProps, inputProps, open, dragging, hint } | — |