- PDF Viewer
- PDF Toolbar
- PDF More Actions Menu
- PDF Floating Toolbar
- PDF Document Tabs
- PDF View
- PDF Document Grid
- PDF Document Info
- PDF Page Navigation
- PDF Zoom Controls
- PDF Undo/Redo Buttons
- PDF Keyboard Shortcuts
- PDF Bookmark Sidebar
- PDF Thumbnail Sidebar
- PDF Search Panel
- PDF Attachment List
- PDF Annotation Layer
- PDF Annotation Toolbar
- PDF Annotation Inspector
- PDF Annotation Selection Menu
- PDF Annotation Sidebar
- PDF Comment
- PDF Comment Draft
- PDF Comment Button
- PDF Comment Sidebar
- PDF Redaction Toolbar
- PDF Capture Button
- PDF Form Fill Toggle
- PDF Signature Button
- PDF Stamp Button
"use client";
import { PdfAttachmentList } from "@/components/pdf-attachment-list";About#
A PDF can carry other files inside it — an embedded spreadsheet, raw data, a signed original. <PdfAttachmentList /> reads those files and gives each one a row and a download button.
Most PDFs have none, so the list shows an empty state rather than collapsing the column out of your layout.
Installation#
pnpm dlx shadcn@latest add pdfcn/attachment-list
Usage#
import { PdfAttachmentList } from "@/components/pdf-attachment-list";<PdfViewer documents={[{ url: "/sample.pdf" }]} className="h-[600px] flex-row">
<PdfAttachmentList className="w-72 shrink-0 border-r" />
<PdfViewerContent />
</PdfViewer><PdfViewer /> is a flex column by default, which stacks the list above the document. Override it with flex-row to put them side by side, and give the list a width — it fills its parent's height but has no width of its own.
The attachment plugin is part of the light tier <PdfViewer /> already registers, so nothing needs to be added to plugins.
Examples#
Default#
"use client";
import { PdfAttachmentList } from "@/components/pdf-attachment-list";Accessibility#
The list is a <ul> of <li> rows, so assistive tech announces its length and position rather than reading a flat run of buttons. Each download button carries an aria-label naming its file — "Download quarterly-figures.csv" rather than a bare "Download" repeated down the column.
A download button disables itself while its bytes are being read, so a slow attachment can't be queued twice.
API Reference#
PdfAttachmentList#
A <ScrollArea /> carrying data-slot="pdf-attachment-list", containing one row per embedded file. Accepts every <ScrollArea /> prop.
Fetches the attachment list once per document through the attachment plugin's capability. It has no width of its own and fills its parent's height.
The list renders in three states, all at the same size: empty while the document loads, an empty state when the document has no attachments, and the rows themselves otherwise.
Downloading#
Unlike <PdfExportButton />, which hands off to the export plugin's own hidden anchor, the attachment plugin returns raw bytes and leaves saving to the caller. The component builds the Blob, the object URL, and the anchor itself, and releases the URL afterwards.
That's also the hook if you want something other than a download — a preview pane, an upload, a hand-off to another app. Call downloadAttachment on the plugin scope and do what you like with the buffer:
const { provides } = useAttachmentCapability();
provides
?.forDocument(documentId)
.downloadAttachment(attachment)
.wait(
(buffer) => {
// ArrayBuffer — save it, parse it, or hand it somewhere else.
},
() => {},
);File icons#
Each row picks an icon from the attachment's MIME type, falling back to the file extension — writers fill the MIME type in inconsistently, and a wrong icon is more noticeable than a generic one. Unrecognized types get a plain file icon.
Styling the rows#
The rendered markup carries slots at each level, so rows can be restyled without forking the component:
| Slot | Element | Description |
|---|---|---|
pdf-attachment-list | div | The scroll container. |
pdf-attachment-items | ul | The list of rows. |
pdf-attachment-item | li | A single attachment row. |
Each row shows the file's name, its description and size when the document declares them, and a download button. Name and description truncate rather than wrap, so rows stay a uniform height in a narrow column.