Component reference
ChatAttachments
Files attached to a message or a draft, with preview, download and remove actions.
Preview
Materials you can revisit
Sent materials keep preview and download; no draft-removal action is offered.
render: () => (
<Measure width="content">
<Stack gap="compact">
<strong>Workspace reading order</strong>
<p>
The attachments are the basis for this discussion. Open the image to see its original
proportions, and download the record to revisit the checked items.
</p>
<ChatAttachments label="Sent materials" items={sampleFiles} />
</Stack>
</Measure>
)Installation
bun add meowtail
Usage
Taken from the examples on this page.
import { ChatAttachments } from "meowtail";
<ChatAttachments
label="Host download callback sample"
items={[
{
id: "host-download",
name: "protected-document.txt",
mediaType: "text/plain",
url: "data:text/plain,Host%20download%20example",
onDownload: () =>
downloadFeedback.set("The host took over the download; local sample only."),
},
]}
/>Examples
The other stories that show this component.
Ratios, sizes, and missing information
Wide images, tall images, and files each use their own carrier. Downloads are built-in samples; no local file is read.
render: () => {
const downloadFeedback = signal("Host download callback sample; no service is connected.");
return (
<div className="workshop-composition">
<ChatAttachments
label="Image ratio samples"
items={[
{
id: "wide",
name: "wide-structure-diagram.svg",
mediaType: "image/svg+xml",
url:
"data:image/svg+xml," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="600" height="120" viewBox="0 0 600 120"><defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#e0e7ff"/><stop offset="1" stop-color="#fae8ff"/></linearGradient></defs><rect width="600" height="120" fill="url(#bg)"/><text x="300.0" y="60.0" text-anchor="middle" dominant-baseline="middle" font-family="system-ui, sans-serif" font-size="22" font-weight="500" fill="#4f46e5" fill-opacity="0.7">600 × 120</text></svg>',
),
},
{
id: "tall",
name: "tall-structure-diagram.svg",
mediaType: "image/svg+xml",
url:
"data:image/svg+xml," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="120" height="360" viewBox="0 0 120 360"><defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#e0e7ff"/><stop offset="1" stop-color="#fae8ff"/></linearGradient></defs><rect width="120" height="360" fill="url(#bg)"/><text x="60.0" y="180.0" text-anchor="middle" dominant-baseline="middle" font-family="system-ui, sans-serif" font-size="16" font-weight="500" fill="#4f46e5" fill-opacity="0.7">120 × 360</text></svg>',
),
},
]}
/>
<ChatAttachments
label="File size samples"
items={[
{
id: "zero",
name: "empty.txt",
mediaType: "text/plain",
sizeBytes: 0,
url: "data:text/plain,",
},
{
id: "kb",
name: "component-design-language-and-all-interaction-state-check-records-with-follow-up-change-suggestions.md",
mediaType: "text/markdown",
sizeBytes: 2860,
url: "data:text/plain,Example",
},
{
id: "mb",
name: "reference.pdf",
mediaType: "application/pdf",
sizeBytes: 1572864,
url: "data:text/plain,Fixture%20metadata%20only",
},
{
id: "unknown",
name: "size-not-provided.txt",
mediaType: "text/plain",
url: "data:text/plain,Unknown%20size",
},
]}
/>
<Note>
File sizes are fixed display data and do not claim to match the actual byte count of the
downloaded content.
</Note>
<ChatAttachments
label="Host download callback sample"
items={[
{
id: "host-download",
name: "protected-document.txt",
mediaType: "text/plain",
url: "data:text/plain,Host%20download%20example",
onDownload: () =>
downloadFeedback.set("The host took over the download; local sample only."),
},
]}
/>
<Note role="status" data-attachment-download-feedback>
{downloadFeedback}
</Note>
<ChatAttachments label="No attachments" items={[]} />
</div>
);
}Attachments, sources and execution states
Images are recognized by thumbnail and files show name and size; detailed process and sources expand on demand.
render: () => (
<div className="workshop-composition">
<ChatAttachments
items={[
{
id: "image",
name: "layout-sketch.svg",
mediaType: "image/svg+xml",
url:
"data:image/svg+xml," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" viewBox="0 0 320 180"><defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#eef2ff"/><stop offset="1" stop-color="#fdf2f8"/></linearGradient></defs><rect width="320" height="180" fill="url(#bg)"/><rect x="20" y="20" width="280" height="172" rx="12" fill="#ffffff" stroke="#e4e4e7"/><circle cx="34" cy="34" r="3.5" fill="#fca5a5"/><circle cx="46" cy="34" r="3.5" fill="#fcd34d"/><circle cx="58" cy="34" r="3.5" fill="#86efac"/><rect x="36" y="56" width="62" height="8" rx="4" fill="#e4e4e7"/><rect x="36" y="72" width="46" height="8" rx="4" fill="#f4f4f5"/><rect x="36" y="88" width="53" height="8" rx="4" fill="#f4f4f5"/><rect x="114" y="52" width="170" height="61" rx="8" fill="#6366f1" fill-opacity="0.14"/><rect x="114" y="125" width="136" height="8" rx="4" fill="#e4e4e7"/><rect x="114" y="141" width="94" height="8" rx="4" fill="#f4f4f5"/></svg>',
),
},
{
id: "file",
name: "component-design-language-and-interaction-states.md",
mediaType: "text/markdown",
sizeBytes: 2468,
url: "data:text/plain,MeowUI%20design%20sample",
onPreview: () =>
(document.getElementById("attachment-preview") as HTMLDialogElement).showModal(),
},
]}
/>
<Dialog
id="attachment-preview"
labelledBy="attachment-title"
describedBy="attachment-description"
>
<DialogPanel
header={
<DialogHeader
title="component-design-language-and-interaction-states.md"
titleId="attachment-title"
description="Built-in Markdown example · No local file was read"
descriptionId="attachment-description"
/>
}
>
<article className="workshop-document">
<h3>Keep content in the foreground</h3>
<p>Components own semantics and actions; themes organize size, color and feedback.</p>
<p>When changing scheme, work in progress stays in place.</p>
</article>
<DialogActions>
<Button data-close-preview>Back to attachments</Button>
</DialogActions>
</DialogPanel>
</Dialog>
<ChatToolCall
icon={<Icon name="file-text" />}
label="Check component states"
state="running"
detail="This is a fixed-state example; no background task runs."
/>
<ChatToolCall
icon={<Icon name="file-text" />}
label="Waiting to confirm the change scope"
state="waiting"
collapsible
>
<p>The component contract stays stable, and themes can evolve independently.</p>
</ChatToolCall>
<ChatToolCall
icon={<Icon name="network" />}
label="Example connection lost"
state="error"
detail="Input is kept; retry once the connection returns."
/>
<ChatSources
items={[
{
title: "Apple Human Interface Guidelines",
url: "https://developer.apple.com/design/human-interface-guidelines/typography",
description:
"Desktop type role reference; the Web parameters are this project's adaptation.",
},
]}
/>
</div>
)API reference
Props
| Prop | Type | Default |
|---|---|---|
itemsRequiredPlain arrays are fixed for the mount. A signal updates rows in place. | MaybeSignal<readonly ChatAttachmentItem[]> | – |
label | string | "Message attachments" |
Also takes className, children and the attributes and events of its root element.
DOM parts
rootimagepreviewfileicondetailsnamesizeentrypreviewButtonremove
Registry
The component is registered as chat-attachments in the meowtail component registry.