FileUpload
Drag-and-drop file upload with image previews, multiple file support, area/button variants, and list/grid layouts.
Playground
Experiment with different props in real-time.
Drop files here or click to upload
Basic Usage
Drop zone with file preview.
Drop files here or click to upload
<script lang="ts">
import { FileUpload } from 'sv5ui';
let files = $state<File[]>([]);
</script>
<FileUpload bind:value={files} />Multiple Files
Allow selecting multiple files.
Drop files here or click to upload
You can upload multiple files at once
<FileUpload multiple label="Drop files here or click to upload" description="You can upload multiple files at once" />Accept Types
Filter by MIME type or extension.
Upload images
PNG, JPG, GIF up to 10MB
Upload document
PDF files only
<!-- Images only -->
<FileUpload accept="image/*" label="Upload images" description="PNG, JPG, GIF up to 10MB" />
<!-- PDF only -->
<FileUpload accept=".pdf" label="Upload document" description="PDF files only" />
<!-- Multiple types -->
<FileUpload accept="image/*,.pdf,.doc,.docx" label="Upload files" description="Images, PDF, or Word documents" />Variants
Area (dropzone) or button style.
Drop files here
<!-- Area variant (default) — dropzone style -->
<FileUpload variant="area" label="Drop files here" />
<!-- Button variant — compact trigger -->
<FileUpload variant="button" label="Choose file" />Sizes
SM upload
MD upload
LG upload
<FileUpload size="sm" label="Small upload" />
<FileUpload size="md" label="Medium upload" />
<FileUpload size="lg" label="Large upload" />Layout
List or grid file display.
List layout
Grid layout
<!-- List layout (default) -->
<FileUpload layout="list" multiple label="List layout" />
<!-- Grid layout -->
<FileUpload layout="grid" multiple accept="image/*" label="Grid layout" />Custom Labels
Upload your avatar
Recommended: 400x400px, PNG or JPG
<FileUpload
icon="lucide:image"
label="Upload your avatar"
description="Recommended: 400x400px, PNG or JPG"
accept="image/*"
/>Validation
Use maxSize (bytes per file) and maxFiles (count cap) to validate uploads. Anything rejected — by accept, maxSize, or maxFiles — never enters value and is reported via onReject.
Upload images
Up to 3 files · max 2 MB each · images only
<script lang="ts">
import { FileUpload, Alert } from 'sv5ui';
import type { FileUploadRejection } from 'sv5ui';
let files = $state<File[]>([]);
let rejected = $state<FileUploadRejection[]>([]);
function reasonLabel(reason: FileUploadRejection['reason']) {
if (reason === 'accept') return 'wrong type';
if (reason === 'maxSize') return 'too large (max 2 MB)';
return 'limit reached (max 3 files)';
}
</script>
<FileUpload
multiple
accept="image/*"
maxSize={2 * 1024 * 1024}
maxFiles={3}
label="Upload images"
description="Up to 3 files · max 2 MB each · images only"
bind:value={files}
onReject={(r) => (rejected = r)}
/>
{#if rejected.length > 0}
<Alert color="error" variant="soft" icon="lucide:alert-triangle"
title={`Rejected ${rejected.length} file(s)`}
>
{#snippet descriptionSlot()}
<ul>
{#each rejected as r, i (i)}
<li><strong>{r.file.name}</strong> — {reasonLabel(r.reason)}</li>
{/each}
</ul>
{/snippet}
</Alert>
{/if}
<!-- The root element exposes data-full when maxFiles is reached.
You can style for it, e.g.:
<FileUpload class="data-full:opacity-60" ... />
-->Inside FormField
Wrap FileUpload in <FormField> — the upload area picks up error state automatically (aria-invalid, red border highlight, and aria-describedby linked to the FormField's description and error text).
Drop files here or click to upload
Please attach at least one document
<script lang="ts">
import { FileUpload, FormField } from 'sv5ui';
let files = $state<File[]>([]);
const error = $derived(files.length === 0 ? 'Please attach at least one document' : '');
</script>
<FormField
label="Attachments"
hint="Required"
required
help="PDF, DOC, or images"
{error}
>
<FileUpload
multiple
accept=".pdf,.doc,.docx,image/*"
bind:value={files}
/>
</FormField>
<!-- When error is set, FileUpload:
- applies aria-invalid + red border highlight
- aria-describedby points to FormField's description and error text
- emits focus/blur/change to the parent <Form> -->Loading & Disabled
Uploading...
Upload disabled
<FileUpload loading label="Uploading..." />UI Slots
Use the ui prop to override classes on internal elements.
| Slot | Description |
|---|---|
root | Root container |
base | Upload area base |
wrapper | Content wrapper |
icon | Upload icon |
label | Label text |
description | Description text |
actions | Actions container |
files | File list container |
file | Individual file item |
fileLeading | File item icon/preview |
fileName | File name text |
fileSize | File size text |
fileTrailing | File item buttons |
Snippets
| Snippet | Description |
|---|---|
leadingSlot | Custom icon/avatar in upload area |
labelSlot | Custom label text |
descriptionSlot | Custom description text |
actionsSlot | Custom action buttons (receives open function) |
filesSlot | Replace entire file list (receives files) |
fileSlot | Custom file item (receives file, index, remove) |
children | Additional content in upload area |
Props
| Prop | Type | Default |
|---|---|---|
value | File[] | [] |
multiple | boolean | false |
accept | string | - |
maxSize | number | - |
maxFiles | number | - |
onReject | (rejected: FileUploadRejection[]) => void | - |
onValueChange | (value: File[]) => void | - |
variant | 'area' | 'button' | 'area' |
color | ColorType | 'primary' |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
layout | 'list' | 'grid' | 'list' |
label | string | 'Drop files here...' |
description | string | - |
icon | string | 'lucide:upload' |
dropzone | boolean | true |
interactive | boolean | true |
preview | boolean | true |
imagePreview | boolean | true |
highlight | boolean | false |
loading | boolean | false |
disabled | boolean | false |
required | boolean | false |
id | string | - |
name | string | - |
ref | HTMLElement | null | null |
class | string | - |
ui | Record<Slot, Class> | - |