# Attachment

A square attachment chip for composer rows — image thumbnails, file glyphs, and upload progress drawn around the card's own border.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





<ComponentPreview name="attachment">
  <AttachmentDemo />
</ComponentPreview>

## Installation [#installation]

```bash
npx shadcn@latest add https://motif-ui.vercel.app/r/attachment.json
```

## Usage [#usage]

```tsx
import { Attachment } from "@/components/ui/attachment";

export function Composer() {
  return (
    <div className="flex flex-wrap gap-4">
      <Attachment name="raven.jpg" src="/raven.jpg" onRemove={remove} />
      <Attachment name="payrollz.xlsx" progress={72} />
      <Attachment name="report.pdf" onRemove={remove} />
    </div>
  );
}
```

## Size [#size]

The chip is 56px, which is the floor — smaller and the name and remove button stop being
usable. There is no `size` prop: the chip is a container query context and every internal
dimension is a share of its own width, so a width in `className` scales the whole thing
(glyph, type, ring, button) proportionally. Text keeps an absolute minimum so it stays
legible at 56px.

## States [#states]

`status` defaults to `uploading` while `progress` is under 100 and `ready` after, so
passing `progress` is usually enough. Every attachment shares one status layer — the
percentage marker, the ring and the remove button; `src` only swaps the glyph for a
preview, which carries its own identity and never shows the name.

| Status      | Renders                                                                                 |
| ----------- | --------------------------------------------------------------------------------------- |
| `uploading` | Progress arc around the card plus the percentage; a preview fades toward the background |
| `ready`     | Glyph and file name, or a bare preview, with the remove button when `onRemove` is set   |
| `error`     | The arc fills and turns `text-destructive`; a file's name follows suit                  |

The remove button stays hidden while uploading — the mock keeps in-flight chips bare, so
cancelling mid-upload is out of scope.

The arc is an SVG `path` tracing the chip's border: it starts at the top centre and runs
clockwise in the theme's `primary`, or `destructive` once an upload has failed. Because a
preview can be any image, its uploading and error states fade it toward `background`
instead of dimming it, so the arc and percentage keep the same contrast as they do on a
glyph chip. The reference mock used a blue accent; swap the token for a literal blue if
you want that hue.

## Glyph [#glyph]

File attachments pick their glyph from the extension — `pdf`/`docx`/`txt` a page,
`xlsx`/`csv` a spreadsheet, `zip`/`tar` an archive, `mp3`/`wav` audio, `mp4`/`mov` video,
`ts`/`js` code, `json`, and images an image — falling back to a plain page for anything
unlisted. Pass `icon` to override it, or `src` to render a real thumbnail instead.

## Component source [#component-source]

<ComponentSource name="attachment" src="registry/new-york/attachment.tsx" title="attachment.tsx" />
