A grid of every open document, each card a navigable single-page preview that opens the document when picked.
"use client";
import { PdfDocumentGrid } from "@/components/pdf-document-grid";About#
<PdfDocumentGrid /> shows every open document as a card in a responsive grid.
Previews come off embedpdf's
thumbnail plugin, which
renders any loaded document by id — so the whole grid runs on the one engine
<PdfViewer> already has, not a viewer per card.
- Preview — each card renders one page of its document. Paging a card is local: it flips through that document's pages without touching the viewer, so the grid works as a browsable overview.
- Select — clicking a card routes it through the
view-manager into the
shared view, so the
<PdfViewer>(and every control bound to it) switches to that document. The selected card carries a ring. - Open more — a trailing card calls the document-manager's file dialog, and the newly opened document joins the grid.
The thumbnail, view-manager, and document-manager plugins all ship inside
<PdfViewer> already, so the grid needs no extra plugin registration.
Installation#
pnpm dlx shadcn@latest add @pdfviewcn/document-grid
Usage#
Place <PdfDocumentGrid> inside a <PdfViewer> loaded with multiple
documents, beside the viewer it drives.
<PdfViewer
documents={[{ url: "/sample.pdf" }, { url: "/form.pdf" }]}
className="h-[720px]"
>
<div className="flex h-full min-h-0">
<PdfDocumentGrid className="w-2/5 border-r" />
<div className="flex min-w-0 flex-1 flex-col">
<PdfToolbar>
<PdfPageNavigation />
<PdfZoomControls />
</PdfToolbar>
<PdfViewerContent />
</div>
</div>
</PdfViewer>Examples#
Default#
Three documents load into the grid, beside a viewer. Page through a card in place, then click it to open that document in the viewer — the ring follows the open document. The trailing card opens another PDF into the grid.
"use client";
import { PdfDocumentGrid } from "@/components/pdf-document-grid";As a launcher#
Pass onSelectDocument to react when a card is picked — the
library app block uses it to swap the grid for a
full-screen reader of the chosen document, then back again.
const [mode, setMode] = React.useState<"library" | "reader">("library");
return mode === "library" ? (
<PdfDocumentGrid onSelectDocument={() => setMode("reader")} />
) : (
<Reader onBack={() => setMode("library")} />
);Accessibility#
Each card is a button labelled Open <name>, with focus-visible rings on both
the preview and the page-step controls. The page steppers are labelled
"Previous page" and "Next page" and stop the click from bubbling into the
card's select, so paging never opens the document. The trailing card is labelled
"Open another PDF".
API Reference#
PdfDocumentGrid#
A <div> carrying data-slot="pdf-document-grid". Enumerates every open
document and renders each as a card. Accepts every prop a <div> takes, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
minCardWidth | number | 200 | Smallest a card gets before the grid drops a column, in pixels. Drives the layout. |
gap | number | 16 | The gutter between cards, in pixels. |
showAddCard | boolean | true | Render the trailing card that opens another document. |
onSelectDocument | (documentId: string) => void | Called with a document's id when its card is picked, after selection is routed. |