File Upload
A dropzone that takes files from all three places people have them: dropped, browsed, and — the one usually missing — pasted.
Walkthrough
1. Basic
One bindable prop. Drop, browse or paste — a screenshot on the clipboard is ⌘V away — and dropped folders are walked for you.
Drag & drop files here
paste with ⌘V
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]);</script><FileUpload bind:files class="max-w-xl" /> 2. Rules
The limits are props, and the hint under the headline writes itself from whichever ones you set. A file that breaks one is named rather than dropped in silence.
Drag & drop files here
image/*, .pdf · up to 2.0 MB · 5 files max · paste with ⌘V
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]);</script><FileUpload bind:files accept="image/*,.pdf" max={5} maxSize={2 * 1000 * 1000} class="max-w-xl" /> 3. Gallery
preview="grid" turns the list into tiles, with the remove button riding on the thumbnail. Images and video get a real preview; anything else shows its extension.
Drop images here
image/* · paste with ⌘V
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]);</script><FileUpload bind:files preview="grid" accept="image/*" label="Drop images here" class="max-w-xl" /> 4. Uploading
Give it an upload function and it becomes an uploader: progress per file, cancel while in flight, retry after a failure, and concurrency in front of the queue. Resolve with whatever the server said — it lands on item.result.
Drag & drop files here
paste with ⌘V
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]); // Stands in for a real request so the demo has something to show. function send( item: UploadFile, { progress, signal }: { progress: (n: number) => void; signal: AbortSignal } ) { return new Promise<{ url: string }>((resolve, reject) => { let at = 0; const tick = setInterval(() => { at = Math.min(100, at + 6 + Math.random() * 10); progress(at); if (at === 100) { clearInterval(tick); resolve({ url: `/uploads/${item.name}` }); } }, 200); signal.addEventListener('abort', () => { clearInterval(tick); reject(new Error('aborted')); }); }); }</script><FileUpload bind:files upload={send} concurrency={2} class="max-w-xl" /> 5. Anywhere
dropAnywhere takes a drop on any part of the page and raises a veil to say so, which is what a document view or an inbox wants — the target is the screen, not a box on it.
Drop a file anywhere on this page
paste with ⌘V
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]);</script><FileUpload bind:files dropAnywhere variant="outlined" size="sm" tone="info" label="Drop a file anywhere on this page" class="max-w-xl"/> 6. Overridden
Every part is reachable: classes names the slots, class covers the root and wins last, and the copy is three plain strings. A height set here beats the padding the size variant ships.
Cover image
1600×900 or larger
<script lang="ts"> import { FileUpload, type UploadFile } from 'omaris'; let files = $state<UploadFile[]>([]);</script><FileUpload bind:files multiple={false} accept="image/*" tone="tertiary" variant="tonal" label="Cover image" hint="1600×900 or larger" browseLabel="Choose" class="max-w-sm" classes={{ zone: 'h-52 rounded-shape-2xl', title: 'text-tertiary', item: 'rounded-full' }}/>