"use client";
import { createPluginRegistration } from "@embedpdf/core";About#
The front end for embedpdf's stamp plugin, which owns the workflow — loading a library of rubber stamps, arming placement of one, and rendering the placed stamp through the annotation layer. This ships as one component with its parts exported:
<PdfStampLibrary />— one library's stamps, each previewed with the plugin's own<StampImg />. Picking one arms placement (forDocument(documentId).activateStampPlacement(libraryId, stamp)); the next click on a page drops it in.<PdfStampPalette />— every loaded library, scrolling, with no container of its own. Registered with no config, the plugin loads a standard set (Approved, Draft, Confidential, and more) from its CDN manifest, so the palette is populated out of the box.<PdfStampDialog />— the palette in a modal, because the next click belongs to the page.<PdfStampButton />— the toolbar toggle that drives the dialog. It stays pressed while a placement is armed, and clicking it then cancels.
Every part reads its own scope off the plugin store rather than through a provider of ours, so any of them mounts alone.
Placed stamps render through the annotation plugin's <PdfAnnotationLayer> — the
stamp plugin registers a rubber-stamp tool there — so both plugins have to be
registered and a layer rendered. That tool is also how a pending placement is
cancelled: the stamp capability has no deactivate of its own, so the button
clears the annotation plugin's active tool instead.
This renders a disabled shell until both AnnotationPluginPackage and
StampPluginPackage are registered on the viewer. The stamp plugin requires
the annotation plugin — registering it alone won't place anything.
Installation#
pnpm dlx shadcn@latest add @pdfviewcn/stamp
Usage#
Register the annotation and stamp plugins.
import { createPluginRegistration } from "@embedpdf/core";
import { AnnotationPluginPackage } from "@embedpdf/plugin-annotation/react";
import { StampPluginPackage } from "@embedpdf/plugin-stamp/react";
// Outside the component — a new array identity on every render tears the
// engine down and rebuilds it, losing scroll and zoom state.
const plugins = [
createPluginRegistration(AnnotationPluginPackage),
createPluginRegistration(StampPluginPackage),
];Render the annotation layer — placed stamps render through it — and drop the button in the toolbar.
<PdfViewer
documents={[{ url: "/sample.pdf" }]}
plugins={plugins}
className="h-[720px]"
>
<PdfToolbar>
<PdfStampButton />
</PdfToolbar>
<PdfViewerContent
pageLayers={({ documentId, pageIndex }) => (
<PdfAnnotationLayer documentId={documentId} pageIndex={pageIndex} />
)}
/>
</PdfViewer>Examples#
Default#
Open the button to see the stamp palette, pick a stamp to arm placement, then click a page to drop it in. Click the button again while it's pressed to cancel placement.
"use client";
import { createPluginRegistration } from "@embedpdf/core";Your own trigger#
<PdfStampDialog /> takes a trigger through children, so the toolbar toggle
isn't the only way in.
<PdfStampDialog>
<DialogTrigger render={<Button variant="outline" />}>Stamp</DialogTrigger>
</PdfStampDialog>Your own container#
The dialog is a convenience, not a requirement. Mount <PdfStampPalette /> in a
popover, a sheet, or a sidebar panel instead — it brings no container of its own.
<Popover>
<PopoverTrigger render={<Button variant="outline" />}>Stamp</PopoverTrigger>
<PopoverContent className="w-80">
<PdfStampPalette />
</PopoverContent>
</Popover>One library#
<PdfStampLibrary /> takes a single library, so a palette can show only the
company's own stamps and leave the standard set out.
function CompanyStamps() {
const { libraries } = useStampLibraries();
const company = libraries.find((library) => library.id === "company");
if (!company) return null;
return <PdfStampLibrary library={company} />;
}Accessibility#
The trigger is a single toggle button labelled "Stamp", with a tooltip to match. Its pressed state reflects whether a placement is armed, and the tooltip switches to explain that clicking a page places the stamp — or clicking the button cancels.
Placing a stamp requires a pointer, since the drop point is chosen by clicking a
page; there's no keyboard equivalent for picking a spot. The palette itself is
keyboard reachable — each stamp is a button focusable in order, named by its
subject. A stamp's preview is a bitmap of its page and carries no text of its
own, so that name is the card's only one: it goes on aria-label and in a
tooltip, rather than in a title a keyboard would never surface.
API Reference#
PdfStampButton#
A <Toggle /> carrying data-slot="pdf-stamp-button", plus the dialog it
drives. Accepts every prop the underlying toggle takes; pressed,
onPressedChange, aria-label, and the default icon are managed internally but
can be overridden.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Whether the stamp dialog is open. |
defaultOpen | boolean | false | Initial open state. Uncontrolled. |
onOpenChange | (open: boolean, eventDetails?) => void | — | Called when the dialog opens or closes. |
The button reads the stamp capability through useStampCapability() and the
armed placement through useActiveStamp(documentId). It renders disabled
whenever there's no active document or the stamp plugin isn't registered.
PdfStampDialog#
<PdfStampPalette /> in a modal, because the next click belongs to the page
rather than to the dialog. Picking a stamp arms placement and closes. Accepts
every <Dialog /> prop; children is the trigger slot.
onOpenChange carries Base UI's eventDetails as an optional second argument —
optional because closing on a pick happens from the inside, where there's no DOM
event to attribute the change to.
PdfStampPalette#
Every loaded library inside a <ScrollArea /> carrying
data-slot="pdf-stamp-palette". Shows a loading line until the CDN manifest
arrives, since the standard stamps are fetched on mount. Accepts every
<ScrollArea /> prop.
| Prop | Type | Default | Description |
|---|---|---|---|
onSelect | (libraryId: string, stamp: StampDefinition) => void | — | Forwarded to every <PdfStampLibrary />. |
PdfStampLibrary#
One library's stamps as a labelled pick-to-place grid, carrying
data-slot="pdf-stamp-library".
| Prop | Type | Default | Description |
|---|---|---|---|
library | StampLibrary | — | One library from useStampLibraries(). Required. |
onSelect | (libraryId: string, stamp: StampDefinition) => void | — | Replaces the default, which arms placement for the picked stamp. |