Attachment
A file or source as a thumbnail, chip or row, with preview, size, upload progress and remove.
Grid
Thumbnails for a sent message. Hover or tab to a tile to reveal its remove button.
Code
<Attachments variant="grid">
<Attachment e:for={f of state.files} e:key={(f) => f.id}
data={f} onRemoveItem={(f) => removeFile(state, f)} />
</Attachments>Usage
import { Attachments, Attachment, AttachmentEmpty, createAttachment, revokeAttachment } from "@elements/ui/attachment";
<Attachments variant="inline">
<Attachment e:for={f of state.files} e:key={(f) => f.id}
data={f} hoverCard
onRemoveItem={(f) => removeFile(state, f)} />
</Attachments>Examples
Inline
Chips for a prompt input. hoverCard shows a large preview on hover or keyboard focus.
Code
<Attachments variant="inline" label="Files to send">
<Attachment e:for={f of state.files} e:key={(f) => f.id}
data={f} hoverCard showMediaType
onRemoveItem={(f) => removeFile(state, f)} />
</Attachments>List
Rows with media type and size. href opens the file.
Code
<Attachments variant="list">
<Attachment e:for={f of files} e:key={(f) => f.id}
data={f} showMediaType showSize href={f.url ?? ""} />
</Attachments>Uploading and errors
progress below 100 shows a bar; error marks the item. Neither changes its size.
Code
// progress 0..100 shows the bar; error marks it failed.
{ id: "u1", filename: "dataset.csv", mediaType: "text/csv", size: 8_600_000, progress: 40 }
<Attachments variant="list">
<Attachment e:for={f of state.uploads} e:key={(f) => f.id} data={f} showSize />
</Attachments>Sources
Source documents from the AI SDK show the globe and their title.
Code
<Attachments variant="inline" label="Sources">
<Attachment data={{ id: "s1", type: "source-document", title: "Elements manual", sourceId: "man" }} />
</Attachments>Custom icon and actions
icon replaces the category icon; the default slot adds actions; removeIcon swaps the remove icon. A broken image falls back to its type icon.
missing.png Code
<Attachment data={file} icon="sparkles">
<Button variant="ghost" size="sm" icon label="Download"><Icon name="download" /></Button>
</Attachment>
<!-- removeIcon swaps the remove button's icon; a broken image falls back to the type icon. -->
<Attachment data={file} removeIcon="trash" onRemoveItem={(f) => remove(f)} />Hover delays and a link
openDelay and closeDelay hold the hover card back; hoverAlign centers it. With href and onRemoveItem, remove sits above the link and never opens it.
Code
<Attachment data={file} href={file.url}
hoverCard openDelay={400} closeDelay={300} hoverAlign="center"
onRemoveItem={(f) => remove(f)} />Default layout
Attachments without a variant lays out as a grid of square tiles.
Code
<!-- variant defaults to grid. -->
<Attachments>
<Attachment e:for={f of files} e:key={(f) => f.id} data={f} />
</Attachments>Empty
AttachmentEmpty stands in when there is nothing to show.
Code
<Attachments e:if={state.files.length > 0}>...</Attachments>
<AttachmentEmpty e:else />
<AttachmentEmpty>Drop files here</AttachmentEmpty>End alignment and classes
hoverAlign end lines the hover card up with the chip's end edge. class is appended to the Attachments, Attachment and AttachmentEmpty roots.
Code
<!-- hoverAlign="end" lines the card's end edge up with the chip. -->
<Attachment data={file} hoverCard hoverAlign="end" />
<!-- class is appended to each root. -->
<Attachments class="my-strip">
<Attachment data={file} class="my-chip" />
</Attachments>
<AttachmentEmpty class="my-strip" />