---
title: File Upload
description: Drag-and-drop file upload area
visibility: guest
draft: false
---

# 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

:::preview{title="Basic File Upload"}
<x-wirekit::file-upload name="document" accept=".pdf,.doc,.docx" hint="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:

1. **File extensions** — start with a dot: `.pdf`, `.docx`, `.csv`
2. **MIME types** — full type string: `application/pdf`, `image/png`
3. **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)* |

:::preview{title="Images only (PNG, JPG, WebP, GIF)"}
<x-wirekit::file-upload
    name="avatar"
    accept="image/png,image/jpeg,image/webp,image/gif"
    label="Drop an image or click to browse"
    hint="PNG, JPG, WebP, or GIF — up to 2 MB"
/>
:::

:::preview{title="Office documents (PDF, Word, Excel)"}
<x-wirekit::file-upload
    name="report"
    accept=".pdf,.doc,.docx,.xls,.xlsx"
    label="Upload a report"
    hint="PDF, Word (.doc/.docx), or Excel (.xls/.xlsx) — up to 10 MB"
/>
:::

:::preview{title="CSV import"}
<x-wirekit::file-upload
    name="customers"
    accept=".csv,text/csv"
    label="Drop a CSV file to import customers"
    hint="UTF-8 encoded CSV — first row is treated as column headers"
/>
:::

> **Tip:** Always pair `accept` with a human-readable `hint` that 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:

```blade
<div class="max-w-md">
    <x-wirekit::file-upload label="Resume" name="resume" accept=".pdf" />
</div>
```

See [Input — Width](/components/input#width) for more layout examples (grid columns, mixed widths).

## Multiple Files

:::preview{title="Multiple Images"}
<x-wirekit::file-upload name="photos" accept="image/*" :multiple="true" label="Drop images or click to browse" hint="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:

:::preview{title="Filename truncation in a narrow container"}
<div style="width: 100%; max-width: 20rem;">
    <x-wirekit::file-upload name="truncation-demo" label="Drop files here" />
</div>
:::

:::source{language="blade"}
<x-wirekit::file-upload name="truncation-demo" label="Drop files here" />
:::

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

:::preview{title="File upload with an error message"}
<x-wirekit::file-upload name="avatar-error-demo" label="Upload your avatar" error="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'` |
| `disabled` | `bool` | `false` | Disabled state |
| `label` | `string` | `'Drop files here or click to browse'` | Dropzone label text |
| `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 (visually `sr-only`) — full a11y support
- `<label>` wraps the dropzone so click + keyboard focus work correctly
- Error message linked via `aria-describedby` + `aria-invalid="true"`
- Upload icon is `aria-hidden="true"` (label text describes the action)

## 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.** Without `accept`, the OS file picker shows everything; users wander into binary files that the server rejects.
- **Don't bind `wire:model.live` to file inputs.** Livewire already streams files via its dedicated upload pipeline — `.live` isn'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 |
| `--padding-wk-x-sm` / `--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:

```php
'components' => [
    'file-upload' => ['size' => 'md', 'multiple' => true, 'accept' => 'image/*'],
],
```

## Further Reading

- [MDN: `<input type="file">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)
- [MDN: HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
- [MDN: `DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer)
- [MDN: `FileList`](https://developer.mozilla.org/en-US/docs/Web/API/FileList)
- [MDN: accept attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#accept)
