File Upload
The <x-wirekit::file-upload> component is a drag-and-drop file dropzone with click-to-browse fallback. It displays a live list of selected files with their sizes.
Usage
Max 5 MB — PDF, DOC, or DOCX
Accepted File Types
The accept prop is passed straight through to the native <input type="file" accept="..."> attribute. The browser uses it as a hint in the file picker (and filters the "All files" vs "specific types" dropdown) — it is NOT a security boundary. A user can always bypass it via drag-and-drop or by choosing "All files". You MUST validate file types server-side in your Laravel validator (e.g. mimes:pdf,docx or mimetypes:application/pdf).
Three ways to specify accepted types
You can mix any of the following inside a single comma-separated string:
- File extensions — start with a dot:
.pdf,.docx,.csv - MIME types — full type string:
application/pdf,image/png - Wildcard groups — generic category:
image/*,video/*,audio/*
Extensions are the friendliest because they match what users see in their file manager, but MIME types and wildcards are more precise and work better on mobile devices.
Common recipes
| Use case | accept value |
|---|---|
| Any image | image/* |
| PNG or JPG only | image/png,image/jpeg or .png,.jpg,.jpeg |
| PDF only | application/pdf or .pdf |
| Office documents | .pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx |
| CSV / spreadsheet import | .csv,.xls,.xlsx,text/csv |
| Any video | video/* |
| Any audio | audio/* |
| Archives | .zip,.tar,.gz,.rar,.7z |
| Camera capture on mobile | image/*;capture=camera |
| Everything (default) | (omit the prop) |
PNG, JPG, WebP, or GIF — up to 2 MB
PDF, Word (.doc/.docx), or Excel (.xls/.xlsx) — up to 10 MB
UTF-8 encoded CSV — first row is treated as column headers
Tip: Always pair
acceptwith a human-readablehintthat repeats the allowed formats and size limit. Users often ignore or miss the file picker filter.
Width
Like all WireKit form components, the file upload fills its container (w-full). Control the width via the parent element:
<div class="max-w-md">
<x-wirekit::file-upload label="Resume" name="resume" accept=".pdf" />
</div>
See Input — Width for more layout examples (grid columns, mixed widths).
Compact Variant
The default shape is a drop area: a block-level dashed rectangle that fills its container. That is the right shape for a field in a form, and the wrong one for a dense row — beside a badge and a couple of size="sm" buttons, a full-width area pushes every neighbor onto its own line.
variant="compact" is the same control at button height. It shrinks to its content, lines up with <x-wirekit::button> on the same size (they share the height, horizontal-padding and radius tokens), and stays a drop target: dragging files onto the compact control works exactly as it does onto the full area.
The label in a compact control
The shipped default label — Drop files here or click to browse — describes an area, so a compact control that used it would be wider than the row it exists to fit. When you pass no label, the compact variant keeps that sentence as the control's accessible name and does not paint it: the result is an icon-sized drop button that a screen reader still announces correctly.
Pass a short label when you want visible text, as the row above does:
Selected files, the hint and the error message all still render below the control — the compact variant changes the control's shape, not the component's anatomy.
Multiple Files
PNG, JPG, GIF up to 10 MB each
When multiple is true, the name attribute is automatically suffixed with [] so Laravel receives an array.
Long Filename Truncation
When a selected file has a name that is longer than the container can hold, the filename is truncated in the middle with an ellipsis (…) instead of wrapping onto a new line or pushing the file size / remove button out of view. This is achieved with truncate (CSS overflow: hidden; text-overflow: ellipsis; white-space: nowrap) combined with min-w-0 on the filename <span>, which lets the flex child shrink below its intrinsic content width.
The preview below sits in a deliberately narrow 20 rem container — drop a file with a long name (e.g. Q4-2026-final-budget-forecast-with-margin-analysis.pdf) into the dropzone to see the ellipsis in action:
The filename span uses truncate min-w-0 so it can shrink below its intrinsic width; the size label and remove button both carry shrink-0 so they stay fully visible regardless of how long the filename is. Widen the container and the filename stops truncating and is shown in full.
Error State
File exceeds the 2 MB limit
Errors are shown below the dropzone with aria-invalid="true" + aria-describedby wiring.
Drag & Drop
Files dropped on the zone are automatically assigned to the underlying <input type="file"> via DataTransfer, and a native change event is fired so Livewire's wire:model bindings work unchanged.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string|null |
null |
Form field name (appends [] when multiple) |
id |
string|null |
auto-generated | Element id |
multiple |
bool |
false |
Allow multiple file selection |
accept |
string|null |
null |
Accepted MIME types or extensions |
size |
string |
'md' |
'sm', 'md', 'lg' |
variant |
string |
'default' |
'default' (full drop area, fills its container) or 'compact' (button-height, shrink-to-fit, for a dense row) |
disabled |
bool |
false |
Disabled state |
label |
string|null |
null → 'Drop files here or click to browse' |
Dropzone label text. In variant="compact" an unset label stays the accessible name but is not painted — see Compact Variant |
removeLabel |
string |
'Remove :name' |
Accessible name template for each file's remove button. The :name placeholder is replaced with the file name at runtime; override it to localize or change the wording |
hint |
string|null |
null |
Helper text below dropzone |
error |
string|null |
null |
Error message (also reads from $errors) |
scope |
string|null |
null |
Scoped personalization key |
Accessibility
- Native
<input type="file">preserved (visuallysr-only) — full a11y support <label>wraps the dropzone so click + keyboard focus work correctly- The focus ring paints on the dropzone itself, not on the input — the input is
sr-only, so a ring drawn on it would sit inside a 1×1 clipped box and never be seen - Error message linked via
aria-describedby+aria-invalid="true" - Upload icon is
aria-hidden="true"(label text describes the action) variant="compact"without alabelkeeps the default sentence assr-onlytext inside the<label>, so the control is never an unnamed icon —sr-onlyclips the text, it does not remove it from the accessibility tree- The compact control carries
wk-touch-target, which adds a centered 44×44 hit area without changing the painted size (WCAG 2.5.5 AAA)
Keyboard Interaction
| Key | Action |
|---|---|
Tab |
Move focus to the file picker control |
Enter / Space |
Open the system file dialog |
Pitfalls
- Don't omit
accept="..."for typed uploads. Withoutaccept, the OS file picker shows everything; users wander into binary files that the server rejects. - Don't bind
wire:model.liveto file inputs. Livewire already streams files via its dedicated upload pipeline —.liveisn't needed and causes redundant requests.
Design Tokens
| Token | Used for |
|---|---|
--text-wk-xs / --text-wk-sm |
Hint / metadata text |
--color-wk-text-muted / --color-wk-text-subtle |
Hint + dropzone instruction text |
--color-wk-bg-muted / --color-wk-bg-subtle |
Dropzone background + chip background |
--color-wk-border-strong |
Dropzone border |
--color-wk-border-error |
Error-state border |
--color-wk-accent |
Active drag-over highlight |
--color-wk-danger-text |
Error message |
--color-wk-ring |
Focus ring |
--ring-wk-width |
Focus ring width |
--radius-wk-sm / --radius-wk-md / --radius-wk-lg |
Dropzone + chip border radius |
--size-wk-sm / --size-wk-md / --size-wk-lg |
Compact-variant control height (same ladder as <x-wirekit::button>) |
--border-wk-width |
Compact-variant border width |
--padding-wk-x-sm / --padding-wk-x-md / --padding-wk-x-lg / --padding-wk-y-xs / --padding-wk-y-sm / --padding-wk-y-md / --padding-wk-y-lg |
Spacing |
--transition-wk-duration |
Drag-over transition |
Config Defaults
The defaults live in config/wirekit.php under components.file-upload. Override them globally:
'components' => [
'file-upload' => ['size' => 'md', 'multiple' => true, 'accept' => 'image/*'],
],