File Upload
A real <input type="file">, wrapped entirely inside a real <label> that IS the drop zone — the whole padded area is natively clickable, no .click()forwarding needed. Drag-and-drop is a progressive enhancement on top of that: the native element has none, and accept only filters the OS dialog, never a drop, so both are reimplemented once and reused by whichever path a file actually arrives through. The real input stays authoritative either way — dropped files are re-assigned onto its own .files via a fresh DataTransfer, so it still participates correctly in a plain, non-JS <form> submission.
Playground
Toggle every adjustable prop and watch the component — and the code — update live.
Usage
<FileUpload label="Upload files" description="PNG or JPG, up to 5MB" accept="image/*" multiple maxFiles={3} maxSize={5242880} errorMessage="That file is too large." /><kernel-file-upload label="Upload files" description="PNG or JPG, up to 5MB" accept="image/*" multiple max-files="3" max-size="5242880" error-message="That file is too large."></kernel-file-upload>Props
| Prop | Type | Default |
|---|---|---|
label | ReactNode (required) | — |
hideLabel | boolean | false |
description | ReactNode | — |
files | File[] | — |
defaultFiles | File[] | [] |
accept | string | — |
multiple | boolean | false |
maxFiles | number | — |
maxSize | number (bytes) | — |
onError | (error) => void | — |
errorMessage | ReactNode | — |
invalid | boolean | false |
disabled | boolean | false |
Rejections (wrong type, over maxSize, or over maxFiles) reject the whole attempted batch and call onError rather than silently keeping only some of what was dropped. Dropping accumulates onto the existing selection; re-opening the OS picker still replaces it, the same as the native element's own behaviour — that inconsistency comes from the browser, not from this wrapper.
Accessibility
- Drag-and-drop is a progressive enhancement only: Tab to the drop zone, Enter or Space opens the native OS dialog, fully usable with zero pointer/drag involvement.
- Each file's remove button gets
aria-label="Remove {filename}"since it's otherwise an icon-only control. - Rejected files go through the same
role="alert"error region as every other Kernel form field'serrorMessage.