Attachment
Users choose, drop, or paste files, and each accepted file appears as a card with a preview and a remove button. Attachment does not upload; your app sends the files.
Drop, paste, or choose files
PNG, JPG, or PDF up to 5 MB. Three files at most.
Installation
Copy the command below and run it in your terminal.
Usage
Bind files on Root to read the selected File objects. Files arrive from the picker, from a drop anywhere on Root, or from a paste
into a focused field inside it. Duplicates and files that break accept, maxSize, or maxFiles are not added and are passed to onReject. Your app owns uploading and
upload status.
While files are dragged over the root, an overlay confirms the drop. When the drag
already breaks a rule the browser can check, such as the file limit or a MIME type in accept, the overlay says why before the
drop. Set addOnPaste={false} to leave pasted files to your own handler.
Composition
List renders a default Item for each file. Pass a children snippet to render your own item, which receives the file. An Item without children renders Preview, Name, Status, and Remove. Compose them yourself to omit,
reorder, or add controls; extra children sit beside the remove button.
Inside a root, Remove removes the file from the bound list and moves focus to a neighboring file, or back to
the trigger when the list empties. Pass onRemove to an item to handle removal yourself. Items animate in and out and the list reflows,
using the theme's motion tokens. Reduced motion turns the animation off.
The list is a grid that fits as many columns of at least 14rem as the space allows and
falls back to one column on narrow screens. Add grid-cols-1 to its class for full-width rows.
Examples
Upload progress
Keep upload state in a map keyed by file and pass it to each item. In this demo the
second file fails its first attempt, and a retry button is composed beside Remove only while the item is in error.
Rejections
Two images, 1 MB each.
Each rejection carries a typed code and a reason sentence such as “This file is larger than the 1.0 MB limit.” Show the reason next to
the trigger. Root also announces added, removed, and rejected files to screen readers.
In a composer
Wrap the composer in Root so files can be dropped anywhere on it and screenshots can be pasted into the prompt. Place List directly before Composer.Input: inside a composer, attachments render as slim chips on the prompt's first line, and the text moves below them once it wraps.
Upload status
Render standalone items outside a root when another part of your interface owns the file list.
Anatomy
Attachment.Root - Manages selected files and accepts drops and pastes.
Attachment.Trigger - Opens the file picker.
Attachment.List - Lists selected files.
Attachment.Item - Holds one file and its upload state.
Attachment.Preview - Shows an image thumbnail or file icon.
Attachment.Name - Shows the file name.
Attachment.Status - Shows size, progress, or the outcome.
Attachment.Remove - Removes the file.
<script lang="ts"> import * as Attachment from '@sivir-ui/svelte/components/attachment'; let files = $state<File[]>([]); </script> <Attachment.Root bind:files accept="image/png,image/jpeg,.pdf" maxFiles={3} maxSize={5 * 1024 * 1024} class="flex w-full max-w-xl flex-col gap-3" > <div class="flex flex-col items-center gap-3 rounded-[var(--radius-lg)] border-[length:var(--border-size)] border-dashed border-border px-6 py-8 text-center" > <div class="flex flex-col gap-1"> <p class="text-sm font-label text-foreground">Drop, paste, or choose files</p> <p class="text-xs text-foreground-muted">PNG, JPG, or PDF up to 5 MB. Three files at most.</p> </div> <Attachment.Trigger variant="outline">Choose files</Attachment.Trigger> </div> <Attachment.List class="grid-cols-1" /> </Attachment.Root>