---
import DownloadIcon from "@lucide/astro/icons/download"
import FileTextIcon from "@lucide/astro/icons/file-text"
import {
Attachment,
AttachmentAction,
AttachmentActions,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
} from "@/components/ui/attachment"
---
<Attachment>
<AttachmentMedia><FileTextIcon /></AttachmentMedia>
<AttachmentContent>
<AttachmentTitle>LICENCE</AttachmentTitle>
<AttachmentDescription>Text document</AttachmentDescription>
</AttachmentContent>
<AttachmentActions>
<AttachmentAction
as="a"
href="https://raw.githubusercontent.com/fulldotdev/ui/main/LICENCE"
download
aria-label="Download LICENCE"
>
<DownloadIcon />
</AttachmentAction>
</AttachmentActions>
</Attachment>Installation
npx shadcn@latest add @fulldev/attachment
Usage
---
import FileIcon from "@lucide/astro/icons/file"
import {
Attachment,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
} from "@/components/ui/attachment"
---
<Attachment>
<AttachmentMedia><FileIcon /></AttachmentMedia>
<AttachmentContent>
<AttachmentTitle>report.pdf</AttachmentTitle>
<AttachmentDescription>2.4 MB</AttachmentDescription>
</AttachmentContent>
</Attachment>
Attachment is a presentation component. It does not select, upload, remove, or persist files.
Examples
Sizes
---
import FileTextIcon from "@lucide/astro/icons/file-text"
import {
Attachment,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
} from "@/components/ui/attachment"
---
<div class="flex flex-col items-start gap-3">
<Attachment size="xs">
<AttachmentMedia><FileTextIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>notes.txt</AttachmentTitle></AttachmentContent
>
</Attachment>
<Attachment size="sm">
<AttachmentMedia><FileTextIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>brief.txt</AttachmentTitle><AttachmentDescription
>18 KB</AttachmentDescription
></AttachmentContent
>
</Attachment>
<Attachment>
<AttachmentMedia><FileTextIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>proposal.txt</AttachmentTitle><AttachmentDescription
>42 KB</AttachmentDescription
></AttachmentContent
>
</Attachment>
</div>Vertical Group
---
import FileIcon from "@lucide/astro/icons/file"
import ImageIcon from "@lucide/astro/icons/image"
import {
Attachment,
AttachmentContent,
AttachmentDescription,
AttachmentGroup,
AttachmentMedia,
AttachmentTitle,
} from "@/components/ui/attachment"
---
<AttachmentGroup class="max-w-md">
<Attachment orientation="vertical">
<AttachmentMedia><ImageIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>cover.jpg</AttachmentTitle><AttachmentDescription
>1.8 MB</AttachmentDescription
></AttachmentContent
>
</Attachment>
<Attachment orientation="vertical">
<AttachmentMedia><FileIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>outline.pdf</AttachmentTitle><AttachmentDescription
>320 KB</AttachmentDescription
></AttachmentContent
>
</Attachment>
</AttachmentGroup>States
States provide visual status only. Your application owns the upload lifecycle and supplies truthful status text.
---
import FileIcon from "@lucide/astro/icons/file"
import {
Attachment,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
} from "@/components/ui/attachment"
---
<div class="flex flex-wrap gap-3">
<Attachment state="idle">
<AttachmentMedia><FileIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>queued.csv</AttachmentTitle><AttachmentDescription
>Ready</AttachmentDescription
></AttachmentContent
>
</Attachment>
<Attachment state="processing">
<AttachmentMedia><FileIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>report.csv</AttachmentTitle><AttachmentDescription
>Processing</AttachmentDescription
></AttachmentContent
>
</Attachment>
<Attachment state="error">
<AttachmentMedia><FileIcon /></AttachmentMedia>
<AttachmentContent
><AttachmentTitle>archive.zip</AttachmentTitle><AttachmentDescription
>File is too large</AttachmentDescription
></AttachmentContent
>
</Attachment>
</div>Native File Input
Use a native file input to let someone choose files. Rendering selected files as attachments requires application state and is separate from this static component.
---
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
---
<div class="grid w-full max-w-sm gap-2">
<Label for="attachments">Choose files</Label>
<Input id="attachments" type="file" multiple />
</div>API Reference
Attachment
| Prop | Type | Default | Description |
|---|---|---|---|
state | "idle" | "uploading" | "processing" | "error" | "done" | "done" | Visual file state. |
size | "xs" | "sm" | "default" | "default" | Controls spacing and type size. |
orientation | "horizontal" | "vertical" | "horizontal" | Controls card layout. |
AttachmentMedia accepts variant="icon" | "image". AttachmentAction uses the Button API and supports as. AttachmentTrigger is polymorphic and covers the card, so give it visible or screen-reader-only link text. All parts forward native attributes.
See the GitHub source code for the component source.