Attachment

Displays a file or image attachment with media, metadata, upload state, and actions.

Installation

npx shadcn add https://hq-ui.vercel.app/r/nova/attachment

Preview

briefing-notes.pdfPDF · 1.4 MB
workspace.png
workspace.pngPNG · 820 KB
customers.csvCSV · 18 KB
renderer.tsxTSX · 12 KB

Features

  • Icon and image media through AttachmentMedia
  • Upload states: idle, uploading, processing, error, and done with built-in styling and a shimmer while in progress
  • Three sizes and horizontal or vertical orientation
  • A full-card AttachmentTrigger that opens a link or dialog while the actions stay independently clickable
  • Scrollable, snapping AttachmentGroup with an edge fade
  • Customizable styling through the className prop on every part

Examples

Image

Set variant="image" on AttachmentMedia and render an <img> inside it. Use orientation="vertical" to stack the media above the content.

attachment-image
Workspace
workspace.pngPNG · 820 KB
Desk
desk-reference.jpgJPG · 1.1 MB
Office
office-reference.jpgJPG · 940 KB

States

Set state to reflect the upload lifecycle. uploading and processing shimmer the title, and error switches to a destructive treatment.

attachment-states
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

Sizes

Use size to switch between default, sm, and xs.

attachment-sizes
Default attachmentPDF · 2.4 MB
Small attachmentPDF · 2.4 MB
Extra small attachment

Group

Wrap attachments in AttachmentGroup to lay them out in a horizontally scrollable, snapping row with an edge fade.

attachment-group
briefing-notes.pdfPDF · 1.4 MB
workspace.png
workspace.pngPNG · 820 KB
customers.csvCSV · 18 KB
renderer.tsxTSX · 12 KB

Trigger

Add an AttachmentTrigger to make the whole card open a link or dialog. It fills the card behind the actions, so the actions stay clickable.

attachment-trigger
research-summary.pdfOpen preview dialog
<Dialog>
  <Attachment>
    {/* media, content, actions */}
    <AttachmentTrigger aria-label="Preview research-summary.pdf" />
  </Attachment>
  <DialogContent>{/* ... */}</DialogContent>
</Dialog>

Accessibility

AttachmentAction renders a Button, and AttachmentTrigger renders a real <button> (or your element via asChild). Follow the guidance below so both are operable and announced.

Label icon-only actions

AttachmentAction is usually icon-only, so give each one an aria-label describing the action and its target.

<AttachmentAction aria-label="Remove sales-dashboard.pdf">
  <XIcon />
</AttachmentAction>

Label the trigger

AttachmentTrigger covers the card with no text of its own, so give it an aria-label for what activating it does.

<AttachmentTrigger
    href={url}
    target="_blank"
    rel="noreferrer"
    aria-label="Open workspace.png"
/>

The trigger sits behind the actions in the stacking order, so an AttachmentAction and the AttachmentTrigger never trap each other — both remain separately focusable and clickable.

Keyboard scrolling

An AttachmentGroup scrolls horizontally. When its attachments are interactive: a trigger or actions, keyboard users reach off-screen items by tabbing to them. For a row of presentational attachments, make the group itself focusable and scrollable by adding tabIndex={0}, role="group", and an aria-label.

Meaning beyond color

The error state uses a destructive color. Keep the failure reason in AttachmentDescription so the state is not conveyed by color alone.

API Reference

Attachment

The root attachment container.

PropTypeDefaultDescription
state"idle" | "uploading" | "processing" | "error" | "done""done"The upload state. Drives styling and the shimmer.
size"default" | "sm" | "xs""default"The attachment size.
orientation"horizontal" | "vertical""horizontal"Lay the media beside or above the content.
classNamestring-Additional classes to apply to the root element.

AttachmentMedia

The media slot for an icon or image preview.

PropTypeDefaultDescription
variant"icon" | "image""icon"Whether the media holds an icon or an <img>.
classNamestring-Additional classes to apply to the media slot.

AttachmentContent

Wraps the title and description.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the content slot.

AttachmentTitle

The attachment name. Shimmers while the attachment is uploading or processing.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the title.

AttachmentDescription

Secondary metadata such as the file type, size, or upload status.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the description.

AttachmentActions

A container for one or more actions, aligned to the end of the attachment.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the actions.

AttachmentAction

An action button. Renders a Button and accepts all of its props.

PropTypeDefaultDescription
sizeButton["size"]"icon-xs"The button size.
...propsReact.ComponentProps<typeof Button>-Props spread to the underlying Button.

AttachmentTrigger

A full-card overlay that activates the attachment. Renders a <button> by default.

PropTypeDefaultDescription
...propsReact.ComponentProps<"button">-Props spread to the trigger element.

AttachmentGroup

Lays out attachments in a horizontally scrollable, snapping row.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the group.