Skip to content

HyFileUpload

Usage examples on this page are written for Lit / plain HTML (<hy-file-upload>). The same component ships as HyFileUpload in @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

html
<hy-file-upload label="Upload" accept="image/*" multiple></hy-file-upload>

With validation

html
<hy-file-upload
  label="Documents"
  accept=".pdf,.doc,.docx"
  max-size="5242880"
  max-files="5"
  multiple
></hy-file-upload>

API

Properties

PropertyAttributeTypeDefaultDescription
filesFile[][]
labellabelstring''Accessible label rendered above the drop zone.
namenamestring''Form control name — files are submitted under this key.
acceptacceptstring''Comma-separated list of allowed MIME types / extensions (.pdf,image/*).
maxSizemax-sizenumber | undefinedMaximum size per file, in bytes.
maxFilesmax-filesnumber | undefinedMaximum number of files allowed in total.
multiplemultiplebooleanfalseAllows selecting more than one file.
disableddisabledbooleanfalseDisables 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).
readonlyreadonlybooleanfalsePrevents 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.
loadingloadingbooleanfalseGlobal pending indicator (e.g., while a batch is uploading).
invalidinvalidbooleanfalseInvalid state — renders the drop zone in a danger variant.
variantvariant'drop-zone' | 'button''drop-zone'Visual form: full-bleed drop-zone (default) or compact button.
sizesize'small' | 'medium' | 'large''medium'Size variant.
directorydirectorybooleanfalseEnables folder selection (sets webkitdirectory on the native input).
helperTexthelper-textstring''Helper text below the drop zone.
errorMessageerror-messagestring''Error message below the drop zone. When present, the component reads as invalid.
barebarebooleanfalseHeadless 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.
formHTMLFormElement | null
validityValidityState

Events

EventDetailDescription
changeAccepted files changed. Detail:
inputFiles were picked/dropped, before validation. Detail:
addA file passed validation. Detail:
removeA file was removed from the list. Detail:
rejectA file was rejected by validation. Detail:

Slots

SlotDescription
defaultDefault slot (bare mode only): content wrapped as the drag-drop target.
placeholderReplaces the default drop-zone empty state.
helper-textHelper text rendered below the drop zone.
error-textError 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

PartDescription
baseThe host wrapper.
labelThe label element.
drop-zoneThe drag target container.
triggerThe "Browse" button / trigger.
placeholderThe empty-state area.
file-listThe container holding all rows.
file-itemA single file row.
file-thumbnailImage preview / file-type icon.
file-progressPer-row upload progress bar.
file-progress-rowRow wrapping the progress bar and its percent label.
file-progress-labelPer-row percent label beside the progress bar.
file-removePer-row remove button.
helper-textHelper text container.
error-messageError message container.

CSS Custom Properties

PropertyDescription
--hy-file-upload-background-restDrop zone background color.
--hy-file-upload-background-drag-overBackground while dragging.
--hy-file-upload-border-restDrop zone border color.
--hy-file-upload-border-drag-overBorder color while dragging.
--hy-file-upload-border-dangerBorder color when invalid.
--hy-file-upload-strokeBorder width.
--hy-file-upload-radiusDrop zone border radius.
--hy-file-upload-padding-blockVertical padding inside drop zone.
--hy-file-upload-padding-inlineHorizontal padding inside drop zone.
--hy-file-upload-row-gapGap between file rows.
--hy-file-upload-row-paddingPadding inside a file row.
--hy-file-upload-row-radiusFile row border radius.
--hy-file-upload-placeholder-icon-sizeSize of the empty-state icon (font-size on hy-icon). Default 2rem.
--hy-file-upload-thumbnail-icon-sizeSize of the per-file thumbnail icon. Default --hy-icon-size-md.
--hy-file-upload-hover-durationTransition duration for hover and focus state feedback
--hy-file-upload-hover-easingTransition easing for hover and focus state feedback
--hy-file-upload-expand-durationTransition duration for the file-list expand and collapse animation
--hy-file-upload-expand-easingTransition 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.

Built with Lit. Documented with VitePress.