Attachment preview
Free · MITA file waiting to be sent, as a chip in the composer: its name, then upload progress, an error, or its size, and a remove button. Images show their preview when the app gives one.
Preview
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/attachment-preview
Usage
"use client"
import { AttachmentChip, AttachmentList } from "@/components/attachment-preview"
import { Message } from "@/components/message"
// A tiny inline image stands in for a thumbnail your app would serve.
const thumb =
"data:image/svg+xml;utf8," +
encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="160" height="96"><rect width="160" height="96" fill="#dbe4ff"/><rect x="16" y="20" width="80" height="10" rx="3" fill="#6b7cff"/><rect x="16" y="40" width="120" height="8" rx="3" fill="#a5b0ff"/><rect x="16" y="56" width="100" height="8" rx="3" fill="#a5b0ff"/></svg>')
export default function Example() {
return (
<div className="flex max-w-2xl flex-col gap-5">
<Message
onOpenAttachment={() => {}}
message={{
id: "u1",
author: { role: "user" },
status: "complete",
blocks: [
{ kind: "attachments", files: [{ id: "a", name: "checkout-error.png", mediaType: "image/png", size: 48_200, previewUrl: thumb }, { id: "b", name: "orders-export.csv", mediaType: "text/csv", size: 2_400_000 }] },
{ kind: "plain", text: "Checkout fails for these orders. Screenshot attached." },
],
}}
/>
<AttachmentList attachments={[{ id: "c", name: "runbook.md", size: 6_100 }, { id: "d", name: "config.json", mediaType: "application/json", size: 812 }]} />
<div className="flex flex-wrap gap-1.5">
<AttachmentChip attachment={{ id: "e", name: "trace.zip", size: 9_800_000, progress: 0.4 }} onRemove={() => {}} />
<AttachmentChip attachment={{ id: "f", name: "huge-video.mov", error: "Too large (max 25 MB)" }} onRemove={() => {}} />
</div>
</div>
)
}<AttachmentChip>
| Prop | Type | Default | Description |
|---|---|---|---|
| attachmentrequired | Attachment | — | The file, with progress (0–1) while uploading or an error if it failed. |
| onRemove | ((id: string) => void) | undefined | — | Shows a remove button. |
| className | string | undefined | — | Extra classes for the outer element. |
<AttachmentPreview>
A sent file, as a tile in a message: an image preview when the app supplied one, else an icon for its type, with its name and size. The name is never a link: with onOpen the tile is a button and your app decides what opening means (a viewer, a signed download).
| Prop | Type | Default | Description |
|---|---|---|---|
| attachmentrequired | Attachment | — | The file. `previewUrl` must be one your app serves (a thumbnail, a blob: URL); it's shown only for images. |
| onOpen | ((attachment: Attachment) => void) | undefined | — | Makes the tile a button that opens the file. |
| className | string | undefined | — | Extra classes for the outer element. |
<AttachmentList>
Several sent files, as a wrapping row of tiles, announced as a list.
| Prop | Type | Default | Description |
|---|---|---|---|
| attachmentsrequired | Attachment[] | — | The files. |
| onOpen | ((attachment: Attachment) => void) | undefined | — | Makes each tile a button that opens its file. |
| className | string | undefined | — | Extra classes for the outer element. |
Functions
formatFileSize(bytes: number | undefined, number: Formatter["number"], t: Translate<typeof attachmentPreviewStrings>)"820 B", "14 KB", "2.4 MB", in the locale.
fileIcon(a: Pick<Attachment, "name" | "mediaType">)An icon for a file, from its media type or, failing that, its extension.
Strings
The fixed words, in English by default. Change any of them under attachmentPreview in <LocaleProvider strings={{ … }}>; the table is exported as attachmentPreviewStrings.
| Key | English |
|---|---|
| remove | Remove {name} |
| open | Open {name} |
| attachments | { one: "{count} attachment", other: "{count} attachments" } |
| bytes | {size} B |
| kilobytes | {size} KB |
| megabytes | {size} MB |