Components / Files
FileUpload
Drag-and-drop zone with validation, per-file progress, cancel and retry.
@ruckc/truss-ui Files
Import
import { FileUpload } from '@ruckc/truss-ui'; Playground
Recipe: parse a file in the browser
upload is just per-file work, so it does not have to touch the network. Parse the file and record the result in your own state. To
reject a file you can handle, resolve with { error: 'message' }: the row is marked failed with that message and nothing is
thrown. Resolving with nothing marks it done, and throwing still marks it failed.
With maxFiles={1} (or multiple={false}) the component works in replace mode: choosing or dropping another file
aborts the current one and swaps it in, so there is no need to remove the old file first. Try a file with only a header row to see the handled
error.
<script lang="ts">
import { FileUpload } from '@ruckc/truss-ui';
import type { UploadItem } from '@ruckc/truss-ui';
let files = $state<UploadItem[]>([]);
let summary = $state('');
async function parse(file: File, onProgress: (pct: number) => void, signal: AbortSignal) {
onProgress(20);
const text = await file.text();
if (signal.aborted) return;
const rows = text.trim().split(/\r?\n/);
// A handled failure: resolve with { error } instead of throwing
if (rows.length < 2) return { error: 'Needs a header row and at least one data row' };
summary = `${rows.length - 1} rows`;
}
</script>
<FileUpload bind:files upload={parse} maxFiles={1} accept=".csv" label="Choose a CSV" /> Recipe: paste, hand over the file, no list
For a dropzone that takes one image and swaps the view, skip the uploader. onfiles receives the files that passed accept, maxSize and maxFiles, whether they were chosen, dropped or pasted; without upload the
items just stay ready. paste takes 'zone' (or true) for the focused zone, or 'document' for the whole page. list={false} hides the file list (a rejected file is still reported in one line), and prompt replaces the visible copy with a string or a snippet while the zone stays a labelled button that opens the picker.
<FileUpload
paste="document"
list={false}
maxFiles={1}
accept="image/*"
prompt="Drop an image here, or paste one"
label="Choose an image"
onfiles={([file]) => open(file)}
/> API
| Prop | Type | Default | Description |
|---|---|---|---|
files bindable | UploadItem[] | [] | The files added so far, with status and progress. Bind it with `bind:files`. |
accept | string | — | e.g. ".csv,image/*,application/pdf" |
multiple | boolean | true | Allow more than one. |
maxSize | number | — | Bytes. |
maxFiles | number | — | Maximum number of files accepted. |
upload | (file: File, onProgress: (pct: number) => void, signal: AbortSignal) => Promise<UploadResult> | — | Your uploader (or any per-file work, e.g. parsing in the browser). Report progress 0–100. Resolve to mark the file done; resolve with `{ error: 'message' }` to mark it failed without throwing; throw to mark it failed too. The signal aborts when the file is removed or replaced. Omit to just collect files. With `multiple={false}` or `maxFiles={1}`, choosing another file replaces the current one. |
label | string | 'Upload files' | Visible label shown above the control. |
hint | string | — | Helper text shown below the control. |
disabled | boolean | false | Disables the control: it cannot be focused, changed or activated. |
paste | boolean | 'zone' | 'document' | false | Accept files from the clipboard (e.g. a pasted screenshot). `true` / `'zone'`: when the drop zone has focus. `'document'`: anywhere on the page (the listener is removed on unmount). Pasted files go through the same `accept`, `maxSize`, `maxFiles` and replace-mode rules as chosen or dropped ones. Pasted text is left alone. |
onfiles event | (files: File[]) => void | — | Called with the files that passed validation, as soon as they are added (choose, drop or paste), with or without `upload`. Without `upload`, the items simply stay `ready`: they never enter an uploading or done state. |
list | boolean | true | Show the list of files below the zone. Set false when you consume the file yourself; a rejected file is then reported in one line under the zone. |
prompt | string | Snippet | — | Replaces the visible "Drop files here or browse" text. The whole zone stays a button that opens the file picker, named by `label`. |