Attachment

A file or image with its name, details, upload state and actions.

View as Markdown
q3-report.pdfPDF · 2.4 MB
<Attachment>  <AttachmentMedia>    <PdfIcon />  </AttachmentMedia>  <AttachmentContent>    <AttachmentTitle>q3-report.pdf</AttachmentTitle>    <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>  </AttachmentContent>  <AttachmentActions>    <AttachmentAction aria-label="Remove q3-report.pdf">      <CancelIcon />    </AttachmentAction>  </AttachmentActions></Attachment>

Installation

$ npx shadcn@latest add @constella/attachment

Usage

import {  Attachment,  AttachmentAction,  AttachmentActions,  AttachmentContent,  AttachmentDescription,  AttachmentGroup,  AttachmentMedia,  AttachmentTitle,  AttachmentTrigger,} from "@/components/ai/attachment";
<Attachment>  <AttachmentMedia>    <FileIcon />  </AttachmentMedia>  <AttachmentContent>    <AttachmentTitle>notes.md</AttachmentTitle>  </AttachmentContent></Attachment>

Composition

AttachmentGroup└── Attachment    ├── AttachmentTrigger    ├── AttachmentMedia    ├── AttachmentContent    │   ├── AttachmentTitle    │   └── AttachmentDescription    └── AttachmentActions        └── AttachmentAction

Size

Use the size prop to fit a composer or a dense list.

quarterly-report.pdfPDF · 2.4 MB
quarterly-report.pdfPDF · 2.4 MB
quarterly-report.pdfPDF · 2.4 MB
<Attachment size="sm">…</Attachment>
SizeMediaTextLines
default40px13pxTwo: title, then description
sm32px13pxTwo: title, then description
xs28px13pxOne: title and description side by side

Keep xs to one short line of content.

Media

Use variant="image" on AttachmentMedia for a picture, with orientation="vertical" on Attachment to stack it above the text. The image fills the media box and stays dimmed until the file is in.

workspace.pngPNG · 820 KB
desk-reference.jpgJPG · 1.1 MB
office-reference.jpgJPG · 940 KB
sales-dashboard.pdfUploading · 64%
message-renderer.tsxTypeScript · 12 KB
<Attachment orientation="vertical">  <AttachmentMedia variant="image">    <img alt="" src={url} />  </AttachmentMedia>  <AttachmentContent>    <AttachmentTitle>workspace.png</AttachmentTitle>    <AttachmentDescription>PNG · 180 KB</AttachmentDescription>  </AttachmentContent></Attachment>

Trigger

Add AttachmentTrigger to open the file from anywhere on the card. Use its render prop for a link or a Dialog's trigger; the actions stay their own buttons above it.

q3-report.pdfPDF · 2.4 MB
<Dialog>  <Attachment>    <AttachmentTrigger      aria-label="Preview q3-report.pdf"      render={<DialogTrigger />}    />    …  </Attachment>  <DialogContent>…</DialogContent></Dialog>

Group

Wrap several in AttachmentGroup for a row that scrolls and snaps, with a Scroll Fade on its edges.

workspace.pngPNG · 820 KB
customers.csvCSV · 18 KB
q3-report.pdfPDF · 2.4 MB
message-renderer.tsxTSX · 12 KB
launch-notes.mdMD · 4 KB
<AttachmentGroup>  {files.map((file) => (    <Attachment key={file.name}>…</Attachment>  ))}</AttachmentGroup>

State

Use the state prop to show where an upload is. Say why a file failed in AttachmentDescription, so the error isn't color alone.

selected-file.pdfReady to upload
design-system.zipUploading · 64%
market-research.pdfProcessing document
financial-model.xlsxUpload failed. Try again.
uploaded-report.pdfUploaded · 1.8 MB
<Attachment state="uploading">  <AttachmentMedia>    <Spinner />  </AttachmentMedia>  <AttachmentContent>    <AttachmentTitle>q3-report.pdf</AttachmentTitle>    <AttachmentDescription>Uploading · 45%</AttachmentDescription>  </AttachmentContent></Attachment>
StateLooks like
idleA dashed edge, ready to upload
uploadingA shimmering title, a dimmed image
processingThe same, while the server reads it
errorA red tint on the media and its note
doneThe default

API Reference

PartAdds
Attachmentstate, size ("default", "sm", "xs"), orientation ("horizontal", "vertical")
AttachmentMediavariant: "default" or "image"
AttachmentTitleShimmers while uploading or processing
AttachmentActionA Button, ghost at icon-xs by default
AttachmentTriggerBase UI render

Every part takes sx, applied last. AttachmentTitle and AttachmentDescription render a span, AttachmentTrigger a button, the rest a div.