Attachment

Purpose and Scope

The Attachment component is the shadcn/ui pattern for showing a file or image together with its preview, name, metadata, upload state, and available actions. The component is documented for chat composers, message threads, and upload lists, which are all interfaces where selected files must remain visible while work continues in the background. In those contexts, the user needs to distinguish one attachment from another, understand whether an upload is still running, and remove or open an item without guessing which part of the card is interactive. Sources: apps/v4/content/docs/components/aria/attachment.mdx, apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Use this page when you are designing the attachment row or thumbnail area of an application and want to follow the first-party composition rather than inventing a one-off file chip. The important distinction is that Attachment is a set of named parts, not only a decorative wrapper around a filename. Media, textual content, action buttons, and an optional full-card trigger each have separate responsibilities. That structure makes the component practical for upload-heavy and AI-style interfaces, where a pending file can move from selected, to uploading, to processing, to complete or failed while the surrounding layout remains stable.

The same public documentation appears in the ARIA, Base UI, and Radix component families. Each page uses the same description, installation command, usage shape, composition tree, features, states, sizes, group behavior, and trigger behavior, while the preview style name changes for the selected family. That repetition is a useful signal: application code should be able to keep the same Attachment vocabulary even when a team chooses a different underlying component library style. Sources: apps/v4/content/docs/components/aria/attachment.mdx, apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Relevant Source Files

  • apps/v4/content/docs/components/aria/attachment.mdx — Defines the ARIA Attachment page, including the public description, CLI and manual installation paths, usage imports, composition tree, feature list, image guidance, state behavior, sizes, group layout, and trigger behavior.
  • apps/v4/content/docs/components/base/attachment.mdx — Mirrors the Attachment contract for the Base UI family with Base-specific previews, showing that the same abstraction is preserved when the selected library is Base UI.
  • apps/v4/content/docs/components/radix/attachment.mdx — Mirrors the Attachment contract for the Radix family with Radix-specific previews, showing that the public component names and documented behaviors remain consistent for Radix-backed components.
  • apps/v4/content/docs/changelog/2024-08-npx-shadcn-init.mdx — Explains the modern CLI direction, including component installation with the add command, component-owned dependencies, aliases, remote registry items, framework support, and monorepo improvements.
  • apps/v4/content/docs/changelog/2025-04-shadcn-2-5.mdx — Explains resolve-anywhere registry installation, where files can be placed outside a fixed structure and imports are resolved through a multi-pass process.
  • apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx — Explains the customization direction behind selecting Radix or Base UI, adapting generated component code, and building a project-specific version of the design system.

Installation and Distribution Model

Install Attachment with the shadcn CLI when you want the component added to your project source. The documented command is:

npx shadcn@latest add attachment

The manual path makes the dependency relationship explicit. Each Attachment page first tells you to install the Button component, then copy the component source into a project file named for the attachment component, and then update import paths to match the application. That sequence follows the repository’s open-code model: the component lands in your codebase, where it can be inspected, edited, themed, and adapted instead of being consumed as an opaque package export. Sources: apps/v4/content/docs/components/aria/attachment.mdx, apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

The CLI changelog gives the broader reason this install flow matters. The rewritten CLI installs components, themes, hooks, utilities, and remote registry items through the add workflow, and a component can ship the dependencies and project updates it needs. For Attachment, that means the documented UI part can be distributed with the supporting Button dependency and with imports aligned to the project’s aliases. If your project uses a different alias prefix or a monorepo layout, the CLI and components configuration should be treated as part of the installation surface, not as an afterthought. Sources: apps/v4/content/docs/changelog/2024-08-npx-shadcn-init.mdx

The later resolve-anywhere registry update is also relevant for teams that customize file layout. Registries are described as being able to place files anywhere in an app while the installer tracks files and resolves imports and aliases in multiple passes. That does not change the Attachment API shown in the docs, but it does affect how confidently you can install or distribute a customized version. A private registry can preserve the same Attachment composition while placing implementation files in the structure used by your product. Sources: apps/v4/content/docs/changelog/2025-04-shadcn-2-5.mdx

Core Composition

The main Attachment card is assembled from small subcomponents. The outer card contains media, content, optional actions, and an optional trigger. The media slot is where an icon or image preview belongs. The content slot usually contains a title and description, with the title carrying the filename and the description carrying type, size, status, or other concise metadata. Actions are grouped separately so that destructive or secondary controls do not become confused with the larger card interaction. Sources: apps/v4/content/docs/components/aria/attachment.mdx

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"
<Attachment>
  <AttachmentMedia>
    <FileTextIcon />
  </AttachmentMedia>
  <AttachmentContent>
    <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>
    <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
  </AttachmentContent>
  <AttachmentActions>
    <AttachmentAction aria-label="Remove sales-dashboard.pdf">
      <XIcon />
    </AttachmentAction>
  </AttachmentActions>
</Attachment>

A good implementation starts with that smallest useful card: one media affordance, one title, one description, and one clear action. Resist the temptation to make the whole surface handle every operation. The docs include a separate full-card trigger for opening a link or dialog, and they state that actions remain independently clickable. That means a remove button should stay a real action even when the rest of the card opens a preview. The result is easier to test with pointer and keyboard input and easier for users to learn.

AttachmentGroup is a related layout primitive for multiple attachments. Instead of treating several attachments as a generic row, the docs specify a horizontally scrollable, snapping row with an edge fade. That behavior is especially useful in compact areas such as chat composers, where files can overflow the available width but should still feel like a connected set. Use the group wrapper when the number of attachments is unknown or when thumbnails and file cards may need to scroll without pushing the rest of the composer out of view. Sources: apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Variants, States, Sizes, and Groups

For file attachments, the common media treatment is an icon inside the media part, with the title and description describing the file. For image attachments, set the media part to the image variant and render an image inside it. The docs also recommend vertical orientation when the media should stack above the content. Use that layout for preview-first cards, such as selected screenshots or uploaded photos, where the visual thumbnail is more important than a compact filename row. Sources: apps/v4/content/docs/components/aria/attachment.mdx

Upload state is controlled through the documented state values: idle, uploading, processing, error, and done. The in-progress states shimmer the title, while error switches to a destructive treatment. These states are presentation signals, not an upload transport. Your application still owns the actual file selection, upload request, processing job, retry behavior, and final persistence. The Attachment state should reflect that lifecycle in a way the user can see immediately, especially when a file is selected but not yet usable. Sources: apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Size and orientation should be chosen from the surrounding workflow. A default-sized card works well in an upload list or message detail view where there is room for metadata. Smaller sizes fit better in dense composers, sidebars, or message bubbles. Horizontal orientation keeps the attachment compact, while vertical orientation gives more room to a preview. Because every part supports custom styling through className, teams can tune density and spacing after installation, but the documented size and orientation props should be the first choices before rewriting layout by hand. Sources: apps/v4/content/docs/components/aria/attachment.mdx, apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Accessibility and Interaction Guidance

The usage example gives the icon-only remove action an explicit accessible label that includes the filename. Follow that pattern for every icon-only AttachmentAction. A visible X icon communicates intent visually to many users, but it does not provide enough information for assistive technology or for users navigating quickly through repeated attachments. A label such as removing a specific file makes the action clear, especially in a group where several attachments have similar controls.

Be careful when combining AttachmentTrigger with action buttons. The docs describe the trigger as filling the card behind the actions, so actions stay clickable. In application code, preserve that separation by making the card trigger open, preview, download, or disclose the attachment, while actions perform immediate operations such as remove or retry. Avoid nested interactive markup that causes the remove action to also activate the card trigger. Test with keyboard focus order as well as pointer clicks, because file attachment areas often become dense once users add more than one item. Sources: apps/v4/content/docs/components/aria/attachment.mdx

Image attachments need the same accessibility care as any image element. If the image is meaningful, provide alternate text that describes the uploaded content or the file’s role in the current workflow. If the image is only decorative because the title and metadata already identify the file, treat it accordingly in the rendered image markup. The Attachment component gives a media slot; your application decides what semantic image content belongs inside that slot based on the context of the upload or message.

API Components Reference

Component or propPurposeNotes
AttachmentOuter attachment cardWraps the media, content, actions, and optional trigger.
AttachmentMediaIcon or preview slotUse variant="image" for image media and render an image inside it.
AttachmentContentText containerUsually contains title and description.
AttachmentTitlePrimary labelTypically the filename; shimmers for in-progress states.
AttachmentDescriptionSecondary metadataUse for type, size, status text, or error information.
AttachmentActionsAction groupContains one or more action controls.
AttachmentActionIndividual controlAdd accessible labels for icon-only actions.
AttachmentTriggerFull-card triggerOpens a link or dialog while actions stay independently clickable.
AttachmentGroupMulti-attachment layoutCreates a scrollable, snapping row with an edge fade.
stateUpload lifecycle stylingDocumented values are idle, uploading, processing, error, and done.
sizeDensity controlDocumented values are default, sm, and xs.
orientationLayout directionUse vertical to stack media above content.
classNameStyling extensionDocumented as supported on every part.

The public surface is intentionally component-oriented. That matches shadcn/ui’s broader direction: you install source code, then adjust it for your product. The create announcement reinforces this by describing customization beyond color tokens, including fonts, spacing, structure, icons, base color, theme, and the chosen component library. For Attachment, keep the documented part names and states as the stable contract, then customize implementation details in the generated code when your design system needs different spacing, iconography, or density. Sources: apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx, apps/v4/content/docs/components/base/attachment.mdx, apps/v4/content/docs/components/radix/attachment.mdx

Implementation Checklist and Next Steps

Start with a minimal file card, then add complexity only when the workflow needs it. Add an upload state when a file is actually moving through a lifecycle, and pair an error state with text and an action that helps the user recover. Use the image variant only when a preview communicates useful information. Wrap multiple items in AttachmentGroup before overflow becomes a layout bug. If the card opens a preview or dialog, add AttachmentTrigger and verify that actions remain reachable independently. Next, review the component family page for your selected ARIA, Base UI, or Radix setup, then review the CLI and registry pages if you plan to distribute a customized Attachment through your own registry.