- 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
A browser-style tab strip for switching between, closing, and opening documents.
"use client";
import { PdfDocumentTabs } from "@/components/pdf-document-tabs";About#
<PdfDocumentTabs /> is the front end for embedpdf's view-manager. A viewer can
load many documents, but view-manager decides which one a view shows — and
this strip is how a person drives that:
- Switch — clicking a tab sets the view's active document, so the toolbar, zoom, search, and content all follow to the new document.
- Close — the tab's close button closes the file through the document
manager (
closeDocument), not just detaches it from the view, matching how embedpdf's own tab bar behaves. The view falls back to a sibling document. - Open — the trailing
+opens another document, which lands as a new tab.
The strip binds to the current view — the focused view by default, or the
enclosing <PdfView>'s view inside a split layout — so the same component works
as a top-level tab bar or per-pane.
Anything you pass as children lands at the strip's trailing edge. That's the slot for actions that belong to the group of tabs rather than to the document one of them selects — splitting the pane, closing it, an overflow menu — which is where VS Code puts the same kind of control.
View-manager ships inside <PdfViewer> already, so these tabs need no extra
plugin registration — just render them.
Installation#
pnpm dlx shadcn@latest add pdfcn/document-tabs
Usage#
Load more than one document and render the strip above the content.
<PdfViewer
documents={[{ url: "/sample.pdf" }, { url: "/form.pdf" }]}
className="h-[720px]"
>
<PdfDocumentTabs />
<PdfToolbar>
<PdfPageNavigation />
<PdfZoomControls />
</PdfToolbar>
<PdfViewerContent />
</PdfViewer>Examples#
Default#
Two documents load as tabs. Click between them and the toolbar acts on whichever
is showing; close one and the view falls back to its sibling; use + to open a
third.
"use client";
import { PdfDocumentTabs } from "@/components/pdf-document-tabs";Pane actions#
In a split layout each pane owns a strip, so the strip is also where that pane's
own controls belong. Children sit at the trailing edge, past the tabs and the
+:
<PdfDocumentTabs>
<PdfMoveToNewPaneButton />
<PdfClosePaneButton />
<PdfToolbarSeparator />
<PdfMoreActionsMenu />
</PdfDocumentTabs>Keep the toolbar below for controls that act on the document — zoom, page navigation, search. The split leaves the toolbar its whole row for the controls that shed under a container query, and it matches how people already read the two rows: tabs and their actions above, document and its actions below. The workspace app block wires it up this way.
Accessibility#
The strip is a Base UI Tabs — the same primitive
ui/tabs wraps — so it gets the
full tab contract rather than an approximation of it. One tab holds the tab stop
and the arrow keys move between them, looping at the ends.
Focus and activation are separate: arrowing onto a tab focuses it without switching documents, and Enter or Space commits. That's the deliberate choice for this strip — activating on focus would render a different PDF on every keypress as you arrowed past.
Each tab's close button is labelled Close {name} and follows the same roving
rule as the tabs, so only the open document's × is a tab stop; ten open
documents cost one, not ten. The trailing button is labelled "Open another PDF"
and sits outside the tab list, since a tab list may only contain tabs.
API Reference#
PdfDocumentTabs#
The Tabs.Root, carrying data-slot="pdf-document-tabs" — controlled off the
view, so the view-manager stays the one source of truth for which document is
showing. Accepts every prop a <div> takes, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
hideSingle | boolean | false | Hide the whole strip while only one document is open. |
showAddButton | boolean | true | Render the trailing button that opens another document. |
children | React.ReactNode | — | Controls pinned to the strip's trailing edge — pane actions, an overflow menu. |
The strip reads the current view through usePdfView() and the view-manager
capability, and the open documents through useOpenDocuments(). Rendered inside
a <PdfView>, it scopes to that pane's view instead of the focused one.
Each tab is a cell carrying data-slot="pdf-document-tab", with data-active
when it's the one showing; its close button carries
data-slot="pdf-document-tab-close", the trailing opener carries
data-slot="pdf-document-tabs-add", and children are wrapped in
data-slot="pdf-document-tabs-actions". Closing calls the document manager's
closeDocument — it closes the file rather than merely detaching it from the
view — and which document takes over afterwards is left to the view-manager.