Skip to content

Forms

IFileUpload ​

A drag-and-drop file field, with a browse button and a list of what has been picked.

The model is always a File[], even without multiple — in that case an array holding at most one file, and picking again replaces it.

Multiple files ​

Image files get a thumbnail and everything else a placeholder of the same size. The object URLs behind the thumbnails are revoked when a file leaves the list or the component unmounts.

Accepted types and sizes ​

accept uses the native syntax — image/*, .pdf, image/png — and is enforced on dragged-in files as well as on the browse dialog.

Put the accepted types in hint: label is the instruction, hint is the fine print.

Capping the count ​

Rejections ​

Refused files raise @reject once per drop, with an array of { file, reason }, where reason is 'type', 'size' or 'count'. The messages shown on the rows come from tooLargeText, wrongTypeText and tooManyText.

Upload progress ​

The component doesn't upload anything, but it can show how your upload is going. statusFor returns a file's state, and the row follows it: a bar and percentage while uploading, "Uploaded" when done, the error and a retry button when it failed. Leave progress out for an indeterminate bar.

A failure is announced to screen readers once. Progress isn't, since reading out every percent would drown everything else.

Invalid and disabled ​

Inside an IFormField the field passes its own validity down, so invalid rarely needs setting by hand.

Props ​

PropTypeDefaultDescription
multipleboolean—Accept more than one file. The model is an array either way
acceptstring—Native syntax: image/*, .pdf, image/png
maxSizenumber—Largest accepted size, in bytes
maxFilesnumber—Cap on how many files may be held at once
disabledboolean—Zone and browse button are inert
invalidboolean—Red border; inherited from IFormField when unset
idstring—Id for the input; IFormField supplies one
labelstring'Drag and drop a file here'Prompt inside the zone
hintstring—Fine print under the prompt
browseLabelstring'Browse files'Text on the browse button
statusFor(file: File) => FileUploadStatus | undefined—Upload state per file, shown on its row
removeLabelstring'Remove'Accessible name for a row's remove action
doneTextstring'Uploaded'Shown after the size once a file is done
retryLabelstring'Retry'Accessible name for a failed row's retry button
failedTextstring'Upload failed'Error shown when a status has no error
tooLargeTextstring'is too large'Message for a size rejection
wrongTypeTextstring'is not an accepted type'Message for a type rejection
tooManyTextstring'exceeds the file limit'Message for a count rejection
unstyledboolean—Drop built-in classes
classstring—Applied to the wrapper stacking the zone above the list
ui{ root?, dropzone?, input?, icon?, label?, browse?, hint?, list?, item?, thumbnail?, placeholder?, details?, name?, meta?, remove?, error?, progress?, actions?, retry? }—Per-element class overrides

Events ​

EventPayloadWhen
rejectFileRejection[]One or more files were refused, once per drop or pick
retryFileThe retry button on a failed row was pressed
ts
interface FileRejection {
  file: File
  reason: 'type' | 'size' | 'count'
}

interface FileUploadStatus {
  state: 'uploading' | 'done' | 'error'
  progress?: number // 0–100
  error?: string
}

Model ​

ts
const files = ref<File[]>([])

The component never uploads anything: it hands you File objects, and posting them is yours. statusFor and @retry let it show how that's going.