HyFileUpload
Usage examples on this page are written for Lit / plain HTML (
<hy-file-upload>). The same component ships asHyFileUploadin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A file upload component with drag-and-drop support, client-side validation, image previews, and full keyboard + screen reader accessibility.
The component does NOT ship a network layer. Consumers wire their own upload transport via the add / change / remove events and the setProgress() / setError() public methods.
disabled suppresses ALL clicks from slotted content — use readonly to gate a bare wrapper. The element is form-associated, so when disabled is set the browser treats it like a native disabled form control and confines trusted click events to its subtree: clicks on anything slotted inside (buttons, links) never propagate past the host, and document-delegated handlers — including every Svelte 5 onclick — silently never fire. Keyboard and input events are NOT suppressed, which masks the bug ("Enter works, clicking doesn't"). This is UA behavior and cannot be opted out of. When the component wraps interactive content (bare mode) and you only want to block file ingestion, set readonly instead — it gates open(), drag-drop, and keyboard activation without touching slotted clicks.
Examples
Basic
<hy-file-upload label="Upload" accept="image/*" multiple></hy-file-upload>With validation
<hy-file-upload
label="Documents"
accept=".pdf,.doc,.docx"
max-size="5242880"
max-files="5"
multiple
></hy-file-upload>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
files | — | File[] | [] | — |
label | label | string | '' | Accessible label rendered above the drop zone. |
name | name | string | '' | Form control name — files are submitted under this key. |
accept | accept | string | '' | Comma-separated list of allowed MIME types / extensions (.pdf,image/*). |
maxSize | max-size | number | undefined | — | Maximum size per file, in bytes. |
maxFiles | max-files | number | undefined | — | Maximum number of files allowed in total. |
multiple | multiple | boolean | false | Allows selecting more than one file. |
disabled | disabled | boolean | false | Disables all interaction. Because the element is form-associated, the UA also suppresses trusted clicks from the entire slotted subtree — never use this on a bare wrapper around interactive content; use readonly there (see the class docs). |
readonly | readonly | boolean | false | Prevents adding/removing files but keeps the list visible. Unlike disabled, slotted content stays fully interactive — the right way to gate ingestion on a bare wrapper. |
loading | loading | boolean | false | Global pending indicator (e.g., while a batch is uploading). |
invalid | invalid | boolean | false | Invalid state — renders the drop zone in a danger variant. |
variant | variant | 'drop-zone' | 'button' | 'drop-zone' | Visual form: full-bleed drop-zone (default) or compact button. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size variant. |
directory | directory | boolean | false | Enables folder selection (sets webkitdirectory on the native input). |
helperText | helper-text | string | '' | Helper text below the drop zone. |
errorMessage | error-message | string | '' | Error message below the drop zone. When present, the component reads as invalid. |
bare | bare | boolean | false | Headless mode. Renders only the default <slot> wrapped as a drag-drop target plus a hidden file input — no drop-zone chrome, no built-in file list, no label / helper. Each picked or dropped file is still validated against accept / maxSize, emitting add / reject, but the component retains no files and participates in no form: the consumer owns all state and UI (cf. react-dropzone, Ant showUploadList={false}). Trigger the picker via the open() method. maxFiles is enforced per drop batch only. |
form | — | HTMLFormElement | null | — | — |
validity | — | ValidityState | — | — |
Events
| Event | Detail | Description |
|---|---|---|
change | — | Accepted files changed. Detail: |
input | — | Files were picked/dropped, before validation. Detail: |
add | — | A file passed validation. Detail: |
remove | — | A file was removed from the list. Detail: |
reject | — | A file was rejected by validation. Detail: |
Slots
| Slot | Description |
|---|---|
default | Default slot (bare mode only): content wrapped as the drag-drop target. |
placeholder | Replaces the default drop-zone empty state. |
helper-text | Helper text rendered below the drop zone. |
error-text | Error text (overrides helperText when present). |
file:<name> | Per-file details rendered inside that file's row, under its name / size / progress — e.g. <div slot="file:Acme-Bold.otf">. The consumer keeps its own state per file (what an uploaded face IS, an image's alt text…) and the component keeps owning the row: thumbnail, transfer caption, progress bar, error, remove. Files that share a name share the slot. React mirror: the renderFileDetails(file) render prop — a slot name that varies per file cannot become a fixed <name>Slot prop, so the divergence is frozen in scripts/api-parity-baseline.json. |
CSS Parts
| Part | Description |
|---|---|
base | The host wrapper. |
label | The label element. |
drop-zone | The drag target container. |
trigger | The "Browse" button / trigger. |
placeholder | The empty-state area. |
file-list | The container holding all rows. |
file-item | A single file row. |
file-thumbnail | Image preview / file-type icon. |
file-progress | Per-row upload progress bar. |
file-progress-row | Row wrapping the progress bar and its percent label. |
file-progress-label | Per-row percent label beside the progress bar. |
file-remove | Per-row remove button. |
helper-text | Helper text container. |
error-message | Error message container. |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-file-upload-background-rest | Drop zone background color. |
--hy-file-upload-background-drag-over | Background while dragging. |
--hy-file-upload-border-rest | Drop zone border color. |
--hy-file-upload-border-drag-over | Border color while dragging. |
--hy-file-upload-border-danger | Border color when invalid. |
--hy-file-upload-stroke | Border width. |
--hy-file-upload-radius | Drop zone border radius. |
--hy-file-upload-padding-block | Vertical padding inside drop zone. |
--hy-file-upload-padding-inline | Horizontal padding inside drop zone. |
--hy-file-upload-row-gap | Gap between file rows. |
--hy-file-upload-row-padding | Padding inside a file row. |
--hy-file-upload-row-radius | File row border radius. |
--hy-file-upload-placeholder-icon-size | Size of the empty-state icon (font-size on hy-icon). Default 2rem. |
--hy-file-upload-thumbnail-icon-size | Size of the per-file thumbnail icon. Default --hy-icon-size-md. |
--hy-file-upload-hover-duration | Transition duration for hover and focus state feedback |
--hy-file-upload-hover-easing | Transition easing for hover and focus state feedback |
--hy-file-upload-expand-duration | Transition duration for the file-list expand and collapse animation |
--hy-file-upload-expand-easing | Transition easing for the file-list expand and collapse animation |
Methods
open()
Programmatically opens the native file picker.
clear()
Removes all files.
removeFile()
Removes a specific file from the list.
setProgress()
Updates per-file progress (0–100). Consumers call this from their upload transport. While progress sits below 100 the row shows an uploading caption (transferred of total) and a percent label beside the bar; at 100 the row settles back to the plain final size.
setError()
Marks a file row with an error message.
clearError()
Clears a previously-set error on a specific file.