Skip to content
Inputs

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.

<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.

<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" />

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.

<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.

<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.

<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'	}}/>