File Upload
Accessible, headless file upload with dropzone, native picker, validation, and previews.
| Package | @vizejs/ui/file-upload |
| Maturity | stable |
| Own the source | vize lib pull file-upload |
| Requires | context, controllable-state, id |
| Aliases | file upload, dropzone, file input, file picker, attachments |
| Covers | HTML input type=file, HTML Drag and Drop API, File and Directory Entries API, Ark UI FileUpload, react-dropzone |
Usage
import { FileUpload, FileUploadRoot, FileUploadClear, FileUploadDropzone, FileUploadItem, FileUploadItemDelete, FileUploadItemGroup, FileUploadItemName, FileUploadItemPreview, FileUploadItemSize, FileUploadTrigger } from "@vizejs/ui/file-upload";
Or copy the source into your project with vize lib pull file-upload (see Source Distribution).
API
FileUpload
Source: src/families/form/file-upload/file-upload-root.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
readonly File[] |
undefined |
Controlled file list. undefined selects uncontrolled behavior. |
defaultValue |
readonly File[] |
[] |
Initial file list for uncontrolled use. |
accept |
string |
undefined |
Native accept list: MIME types, type/* wildcards, and .ext suffixes. |
multiple |
boolean |
false |
Allow more than one file. Single uploads replace the current file. |
maxFiles |
number |
Infinity |
Maximum number of files held at once. Ignored unless multiple is set. |
maxSize |
number |
undefined |
Maximum accepted byte size per file. |
minSize |
number |
undefined |
Minimum accepted byte size per file. |
disabled |
boolean |
false |
Disable picking, dropping, pasting, and removing files. |
required |
boolean |
false |
Require at least one file for native form validation. |
name |
string |
undefined |
Native input name used for form submission. |
form |
string |
undefined |
Id of a form owner outside the root's ancestor chain. |
directory |
boolean |
false |
Let the native picker choose whole directories (webkitdirectory). |
capture |
"environment" | "user" |
undefined |
Native capture hint for mobile camera and microphone pickers. |
validate |
FileUploadValidator |
undefined |
Custom validation run after the built-in type and size checks. |
messages |
FileUploadMessages |
undefined |
Replacement factories for built-in rejection messages. |
locale |
string |
undefined |
BCP 47 locale used for size formatting. undefined formats with en-US, keeping server and client output identical; pass the app locale (e.g. from useLocale()). |
sizeStandard |
FileUploadSizeStandard |
"si" |
Unit convention used by the default size formatter. |
formatSize |
FileUploadSizeFormatter |
undefined |
Replacement byte-size formatter. |
previewAccept |
string |
"image/*" |
accept-syntax filter for files that receive client-side object-URL previews. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[files: readonly File[]] |
Requested file list for v-model. |
change |
[files: readonly File[], previous: readonly File[]] |
Fired after any distinct file-list request. |
accept |
[files: readonly File[], source: FileUploadSource] |
Fired with the files accepted from one pick, drop, paste, or API request. |
reject |
[rejections: readonly FileUploadRejection[], source: FileUploadSource] |
Fired with the files rejected from one pick, drop, paste, or API request. |
invalid |
[nativeEvent: Event] |
Fired when native form validation reports the hidden input invalid. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadSlotState |
Compound FileUpload parts. Receives the file list, availability, and actions. |
Exposed
| Member | Type | Description |
|---|---|---|
addFiles |
— | |
clear |
— | |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
files |
— | |
getRelativePath |
— | |
id |
— | |
inputElement |
— | |
openPicker |
— | |
removeFile |
— | |
reset |
— | |
state |
— |
FileUploadRoot
Source: src/families/form/file-upload/file-upload-root.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
readonly File[] |
undefined |
Controlled file list. undefined selects uncontrolled behavior. |
defaultValue |
readonly File[] |
[] |
Initial file list for uncontrolled use. |
accept |
string |
undefined |
Native accept list: MIME types, type/* wildcards, and .ext suffixes. |
multiple |
boolean |
false |
Allow more than one file. Single uploads replace the current file. |
maxFiles |
number |
Infinity |
Maximum number of files held at once. Ignored unless multiple is set. |
maxSize |
number |
undefined |
Maximum accepted byte size per file. |
minSize |
number |
undefined |
Minimum accepted byte size per file. |
disabled |
boolean |
false |
Disable picking, dropping, pasting, and removing files. |
required |
boolean |
false |
Require at least one file for native form validation. |
name |
string |
undefined |
Native input name used for form submission. |
form |
string |
undefined |
Id of a form owner outside the root's ancestor chain. |
directory |
boolean |
false |
Let the native picker choose whole directories (webkitdirectory). |
capture |
"environment" | "user" |
undefined |
Native capture hint for mobile camera and microphone pickers. |
validate |
FileUploadValidator |
undefined |
Custom validation run after the built-in type and size checks. |
messages |
FileUploadMessages |
undefined |
Replacement factories for built-in rejection messages. |
locale |
string |
undefined |
BCP 47 locale used for size formatting. undefined formats with en-US, keeping server and client output identical; pass the app locale (e.g. from useLocale()). |
sizeStandard |
FileUploadSizeStandard |
"si" |
Unit convention used by the default size formatter. |
formatSize |
FileUploadSizeFormatter |
undefined |
Replacement byte-size formatter. |
previewAccept |
string |
"image/*" |
accept-syntax filter for files that receive client-side object-URL previews. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[files: readonly File[]] |
Requested file list for v-model. |
change |
[files: readonly File[], previous: readonly File[]] |
Fired after any distinct file-list request. |
accept |
[files: readonly File[], source: FileUploadSource] |
Fired with the files accepted from one pick, drop, paste, or API request. |
reject |
[rejections: readonly FileUploadRejection[], source: FileUploadSource] |
Fired with the files rejected from one pick, drop, paste, or API request. |
invalid |
[nativeEvent: Event] |
Fired when native form validation reports the hidden input invalid. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadSlotState |
Compound FileUpload parts. Receives the file list, availability, and actions. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLDivElement | null |
Rendered root element. |
inputElement |
HTMLInputElement | null |
Hidden native file input used for picking and form submission. |
id |
string |
Root-owned base id. |
files |
readonly File[] |
Current file list. |
state |
FileUploadState |
Stable state token. |
openPicker |
() => void |
Opens the native file picker unless disabled. |
addFiles |
(files: Iterable<File>) => FileUploadAddResult |
Validates and adds candidate files as a programmatic (api) request. |
removeFile |
(file: File) => boolean |
Removes one file and reports whether the list changed. |
clear |
() => boolean |
Removes every file and reports whether the list changed. |
reset |
() => boolean |
Restores the default file list and reports whether the list changed. |
getRelativePath |
(file: File) => string | null |
Relative path recorded for a file dropped inside a directory, or null. |
FileUploadClear
Source: src/families/form/file-upload/file-upload-clear.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel |
string |
undefined |
Accessible name when the slot has no visible text. |
Events
| Event | Payload | Description |
|---|---|---|
click |
[nativeEvent: MouseEvent] |
Fired before files are cleared. Call preventDefault() to keep them. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadActionSlotState |
Button contents. Receives availability and file count. |
Exposed
| Member | Type | Description |
|---|---|---|
count |
— | |
disabled |
— | |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
FileUploadDropzone
Source: src/families/form/file-upload/file-upload-dropzone.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned dropzone id. null and undefined derive one from the root id. |
openOnClick |
boolean |
true |
Treat the dropzone as a button that opens the native picker on click, Enter, and Space. When false the dropzone is only a drop and paste target without a focus stop. |
paste |
FileUploadPasteScope |
"self" |
Where clipboard files are accepted: the focused dropzone, the whole document, or nowhere. document listens while mounted and enabled, and ignores pastes into editable controls (inputs, textareas, contenteditable) outside the dropzone so text pasting keeps working. |
rejectDragEffect |
FileUploadRejectDragEffect |
"none" |
dropEffect announced during dragover while the drag preview is rejected. none shows the not-allowed cursor and makes the browser refuse the drop; copy lets the drop happen so it produces typed rejections. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the dropzone. |
ariaDescribedby |
string |
undefined |
Space-separated ids of hints such as accepted types and size limits. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadDropzoneSlotState |
Dropzone contents. Receives drag and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
dragging |
boolean |
Whether a file drag is over the dropzone. |
rejecting |
boolean |
Whether the dragged items would be rejected. |
disabled |
boolean |
Whether interaction is disabled. |
state |
FileUploadDropzoneState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered dropzone element. |
id |
string |
Deterministic dropzone id. |
focus |
(options?: FocusOptions) => void |
Moves focus to the dropzone. |
FileUploadItem
Source: src/families/form/file-upload/file-upload-item.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
file (required) |
File |
required |
File described by this item and its parts. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemSlotState |
Item contents. Receives per-file name, size, type, path, and preview state. |
FileUploadItemDelete
Source: src/families/form/file-upload/file-upload-item-delete.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel |
string |
undefined |
Accessible name when the slot has no visible text, for example Remove report.pdf. |
Events
| Event | Payload | Description |
|---|---|---|
click |
[nativeEvent: MouseEvent] |
Fired before the file is removed. Call preventDefault() to keep it. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemSlotState |
Button contents. Receives per-file state. |
FileUploadItemGroup
Source: src/families/form/file-upload/file-upload-item-group.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel |
string |
undefined |
Accessible name for the file list. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the file list. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemGroupSlotState |
List contents, usually one FileUploadItem per entry of items. |
FileUploadItemName
Source: src/families/form/file-upload/file-upload-item-name.vue
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemSlotState |
Replaces the file name text. Receives per-file state. |
FileUploadItemPreview
Source: src/families/form/file-upload/file-upload-item-preview.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
alt |
string |
"" |
Alternative text for the default <img>. Empty marks the preview decorative because FileUploadItemName already names the file. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemPreviewSlotState |
Replaces the default <img>. Receives the object URL once created on the client. |
FileUploadItemSize
Source: src/families/form/file-upload/file-upload-item-size.vue
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadItemSlotState |
Replaces the formatted size text. Receives per-file state. |
FileUploadTrigger
Source: src/families/form/file-upload/file-upload-trigger.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel |
string |
undefined |
Accessible name when the slot has no visible text. |
ariaDescribedby |
string |
undefined |
Space-separated ids of hints such as accepted types and size limits. |
Events
| Event | Payload | Description |
|---|---|---|
click |
[nativeEvent: MouseEvent] |
Fired before the picker opens. Call preventDefault() to keep it closed. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
FileUploadActionSlotState |
Trigger contents. Receives availability and file count. |
Exposed
| Member | Type | Description |
|---|---|---|
count |
— | |
disabled |
— | |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
Behavior
Normative state x input -> outcome table for @vizejs/ui/file-upload:
file-upload-root.vue, file-upload-dropzone.vue, file-upload-trigger.vue,
file-upload-item-group.vue, file-upload-item.vue, file-upload-item-preview.vue,
file-upload-item-name.vue, file-upload-item-size.vue, file-upload-item-delete.vue,
file-upload-clear.vue, and the pure helpers in file-upload-validation.ts and
file-upload-transfer.ts. Every row is proven by the named test.
| ID | State | Input | Outcome | Evidence |
|---|---|---|---|---|
| F1 | default | render | root, role="button" dropzone, hidden native <input type="file">, and <ul> expose deterministic ids, name/accept/multiple/required, and data |
renders dropzone, hidden native input, and list semantics with deterministic ids |
| F2 | enabled | trigger click, dropzone click/Enter/Space | each activation opens the native picker exactly once; a nested trigger does not also open through the dropzone | trigger, click, Enter, and Space open the native picker once each |
| F3 | multiple | native picker change |
validated files append, update:modelValue/change/accept("input") fire with the same File objects, and the native FileList mirrors the value |
picker changes append validated files, sync the native FileList, and render items |
| F4 | file drag over dropzone | nested dragenter/dragleave |
a depth counter keeps data-dragging until the outermost leave; payloads whose item types fail accept set data-drag-reject and announce dropEffect rejectDragEffect (default none) instead of copy; non-file drags are ignored |
drag enter and leave track nesting and flag rejected payloads |
| F5 | multiple with limits | drop |
accepted files append up to maxFiles; each rejected file reports every file-invalid-type/file-too-large/too-many-files error via reject("drop") |
drops add accepted files and emit typed rejections |
| F6 | directory drop | drop with webkitGetAsEntry() |
directories expand depth-first across batched readEntries() calls; nested files expose relativePath, getRelativePath(), and a name title |
dropped directories expand recursively and keep relative paths |
| F7 | paste="self" / "document" |
clipboard paste with files |
files are added with source paste from the dropzone, or from anywhere in the document except editable controls outside the dropzone; the document listener binds after mount and is released while disabled and on unmount |
paste adds clipboard files on the dropzone or the whole document outside editable controls |
| F8 | single (default) | pick or drop | one valid file replaces the current file; several files at once are all rejected as too-many-files |
single uploads replace the current file |
| F9 | previewable files, client | mount, remove, unmount | object URLs are created lazily after mount only for previewAccept matches and revoked when the file leaves the value or the root unmounts |
previews create object URLs on the client and revoke them on removal and unmount |
| F10 | files present | item delete / clear | files are removed and focus moves to the neighbouring delete button, or to the dropzone when the list empties | removing and clearing files keeps focus inside the upload |
| F11 | disabled | click, drag, drop, paste, delete, API | nothing opens or changes; dropzone leaves the tab order with aria-disabled, buttons and the native input are disabled |
disabled uploads ignore picking, dropping, pasting, and removal |
| F12 | controlled | picker change |
update:modelValue requests the next list while rendered files follow the parent until it accepts |
controlled value wins until the parent accepts the request |
| F13 | exposed instances | addFiles, removeFile, clear, reset, openPicker, focus |
imperative API validates as source api, reports changes, unwraps reactive props to the consumer's File, and exposes element refs |
exposes typed state and imperative file controls |
| F14 | required, empty |
native invalid |
the event is re-emitted as invalid and focus moves to the dropzone, since the hidden input cannot take focus |
native invalid events are re-emitted and move focus to the dropzone |
| F15 | locale | render sizes | sizes use the locale prop, then en-US; sizeStandard="iec" and formatSize replace units |
formats sizes with the locale prop, IEC units, or a custom formatter |
| F16 | missing provider | setup | root-bound and item-bound parts fail closed with the shared context diagnostic | compound parts require a matching root provider |
| F17 | SSR | render twice | markup is byte-identical, generated ids are stable, and no object URL or <img> is produced on the server |
renders byte-identical file upload markup across isolated SSR requests |
| F18 | SSR | hydrate | server nodes are reused without diagnostics and previews appear only after mount | hydrates the file upload without mismatches and creates previews after mount |
| V1 | accept parsing |
parse | comma tokens normalize to any/extension/mime, parameters are dropped, and invalid tokens are ignored like the browser does |
parses native accept lists into normalized tokens and drops invalid tokens |
| V2 | accept matching |
match file | type/* wildcards, exact MIME essences, and case-insensitive multi-dot extensions match; empty lists accept all |
matches files by mime wildcard, exact mime, and case-insensitive extension |
| V3 | drag types | match type only | a type is accept, reject, or unknown when empty or when only extensions could decide |
decides dragged item types as accept, reject, or unknown |
| V4 | size formatting | format bytes | SI uses localized Intl units; IEC uses powers of 1024 with KiB suffixes joined by a no-break space; invalid sizes format as zero |
formats sizes with SI locale units and IEC suffixes |
| V5 | file cap | resolve | single uploads cap at one; maxFiles floors, clamps at zero, and defaults to unlimited |
resolves the effective file cap |
| V6 | per-file limits | validate | type, max size, and min size failures are all reported with default messages | reports every per-file failure in check order with default messages |
| V7 | single | validate | a valid candidate replaces the current file; simultaneous candidates are all too-many-files |
single uploads replace the current file and reject simultaneous candidates |
| V8 | multiple | validate | earliest valid candidates fill remaining capacity; overflow is too-many-files |
multiple uploads append up to maxFiles and reject the overflow |
| V9 | custom validator | validate | built-in codes reuse default messages, other strings become custom, error objects pass through, and the validator sees files accepted so far |
custom validators map codes, strings, and error objects |
| V10 | messages | validate | messages factories and formatSize replace default copy |
message factories and size formatters replace default copy |
| T1 | transfer | inspect types | payloads are file drags only when types contains Files |
recognizes file payloads by the Files drag type |
| T2 | transfer | predict rejection | rejection is predicted from definite type mismatches and capacity, never from undecidable items | predicts drag rejection from item types and capacity only |
| T3 | transfer | collect files | item files are captured synchronously, with a fallback to files |
collects plain item files and falls back to the files list |
| T4 | transfer | collect directories | directory entries are read until an empty batch, depth-first, recording fullPath-relative paths |
expands dropped directories depth-first across batched readers with relative paths |
The hidden native input keeps the value form-associated: after every change its
FileList is rebuilt with DataTransfer where the engine allows it, required
is only set while the value is empty, and directory maps to webkitdirectory.
Preview URLs, focus movement, and document listeners are client-only and are
released with the owning effect scope. Consumers must not nest interactive
content other than FileUploadTrigger inside a clickable dropzone.