EmbedPDF

Stamp Plugin

The Stamp Plugin extends the Annotation Plugin with reusable rubber-stamp libraries. Instead of treating every stamp as a one-off image, you can preload curated stamp sets, let users browse and place them, and even turn annotations they create in the viewer into reusable stamps at runtime.

Out of the box, the plugin is designed around two workflows:

  • Using preloaded libraries for common review stamps like Approved, Draft, or Confidential.
  • Creating custom libraries from annotations or imported PDFs, then exporting those libraries for later reuse.
ℹ️

Stamps render through the existing AnnotationLayer. In React, StampPluginPackage already registers the preview renderer used while the user is hovering a stamp over the page, so you do not need to add a separate layer component.

Installation

The Stamp Plugin builds on top of the Annotation Plugin and its interaction dependencies. The i18n plugin is optional, but recommended if you want the default manifest-backed stamp library to follow the active locale.

npm install @embedpdf/plugin-stamp @embedpdf/plugin-annotation @embedpdf/plugin-interaction-manager @embedpdf/plugin-selection @embedpdf/plugin-history

Optional localization support:

npm install @embedpdf/plugin-i18n

Registration

Import StampPluginPackage, the Annotation Plugin, and the Annotation Plugin dependencies. Register the dependencies first, then the annotation plugin, then the stamp plugin.

By default, the stamp plugin:

  • Loads a localized standard stamp manifest from @embedpdf/default-stamps.
  • Creates a writable custom library the first time you save a user-generated stamp.
import { createPluginRegistration } from '@embedpdf/core' import { EmbedPDF } from '@embedpdf/core/react' // ... other imports import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/react' import { SelectionPluginPackage } from '@embedpdf/plugin-selection/react' import { HistoryPluginPackage } from '@embedpdf/plugin-history/react' import { AnnotationPluginPackage } from '@embedpdf/plugin-annotation/react' import { StampPluginPackage } from '@embedpdf/plugin-stamp/react' const plugins = [ // ... essential viewer plugins like DocumentManager, Viewport, Scroll, Render createPluginRegistration(DocumentManagerPluginPackage, { /* ... */ }), createPluginRegistration(ViewportPluginPackage), createPluginRegistration(ScrollPluginPackage), createPluginRegistration(RenderPluginPackage), // Annotation dependencies first createPluginRegistration(InteractionManagerPluginPackage), createPluginRegistration(SelectionPluginPackage), createPluginRegistration(HistoryPluginPackage), // Annotation must be registered before stamps createPluginRegistration(AnnotationPluginPackage, { annotationAuthor: 'Jane Doe', }), // StampPluginPackage registers the hover preview utility for React createPluginRegistration(StampPluginPackage, { defaultLibrary: { id: 'custom', name: 'Custom Stamps', categories: ['custom', 'sidebar'], }, }), ]

Usage

1. Render Stamps Through <AnnotationLayer />

Stamp annotations are created, previewed, and selected through the Annotation Plugin. That means your page renderer should look just like a normal annotation-enabled viewer: wrap the page in PagePointerProvider, render the document, then render SelectionLayer and AnnotationLayer.

import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/react' import { AnnotationLayer } from '@embedpdf/plugin-annotation/react' <Scroller documentId={activeDocumentId} renderPage={({ pageIndex }) => ( <PagePointerProvider documentId={activeDocumentId} pageIndex={pageIndex} > <RenderLayer documentId={activeDocumentId} pageIndex={pageIndex} /> <SelectionLayer documentId={activeDocumentId} pageIndex={pageIndex} /> <AnnotationLayer documentId={activeDocumentId} pageIndex={pageIndex} /> </PagePointerProvider> )} />

2. Build a Stamp Sidebar

The React package includes hooks and components for building your own stamp browser:

  • useStampLibraries() gives you the loaded libraries.
  • useStampsByLibrary(libraryId, category) returns the stamps you want to display.
  • useActiveStamp(documentId) tells you which stamp is currently armed for placement.
  • useStampCapability() gives you the plugin API.
  • StampImg renders a stamp thumbnail from a library page.
import { StampImg, useActiveStamp, useStampCapability, useStampLibraries, useStampsByLibrary, type StampDefinition, } from '@embedpdf/plugin-stamp/react' const StampSidebar = ({ documentId, selectedLibraryId }) => { const { libraries } = useStampLibraries() const { provides: stampCapability } = useStampCapability() const stamps = useStampsByLibrary(selectedLibraryId, 'sidebar') const activeStamp = useActiveStamp(documentId) const handleStampClick = (libraryId: string, stamp: StampDefinition) => { stampCapability ?.forDocument(documentId) .activateStampPlacement(libraryId, stamp) } return ( <div> {libraries.map((library) => ( <div key={library.id}>{library.name}</div> ))} {stamps.map(({ library, stamp }) => { const isActive = activeStamp?.libraryId === library.id && activeStamp.stamp.id === stamp.id return ( <button key={`${library.id}-${stamp.id}`} onClick={() => handleStampClick(library.id, stamp)} > <StampImg libraryId={library.id} pageIndex={stamp.pageIndex} width={120} /> <span>{stamp.subject}</span> {isActive && <span>placing</span>} </button> ) })} </div> ) }

When a user clicks a stamp in your sidebar, the plugin switches the annotation tool to the rubber-stamp tool and shows a live preview as they move the pointer over the page.

3. Create a Custom Stamp From an Existing Annotation

If your users can draw annotations, you can turn those annotations into reusable stamps. This is a good fit for approval marks, signatures, custom seals, or reusable review graphics.

import { PdfAnnotationName } from '@embedpdf/models' import { useAnnotation } from '@embedpdf/plugin-annotation/react' import { useStampCapability } from '@embedpdf/plugin-stamp/react' const CreateStampButton = ({ documentId }: { documentId: string }) => { const { provides: annotation } = useAnnotation(documentId) const { provides: stampCapability } = useStampCapability() const handleCreateStamp = () => { const selected = annotation?.getSelectedAnnotation() if (!selected || !stampCapability) return stampCapability .forDocument(documentId) .createStampFromAnnotation( selected.object, { name: PdfAnnotationName.Custom, subject: 'Reviewed by Legal', categories: ['custom', 'sidebar'], }, 'custom', ) } return <button onClick={handleCreateStamp}>Create Stamp From Selection</button> }

The plugin exports the selected annotation’s appearance into a one-page PDF and stores that page in the target library. If the target library does not exist yet, the configured defaultLibrary is created automatically.

4. Export a Custom Library

Once users have built up a custom library, you can export it as a PDF and persist the accompanying metadata however you like.

import { useStampCapability } from '@embedpdf/plugin-stamp/react' const ExportButton = () => { const { provides: stampCapability } = useStampCapability() const handleExport = () => { stampCapability?.exportLibrary('custom').wait((exported) => { console.log(exported.name) // "Custom Stamps" console.log(exported.pdf) // ArrayBuffer with one page per stamp console.log(exported.stamps) // Stamp metadata for each page }) } return <button onClick={handleExport}>Export Custom Library</button> }

exportLibrary() returns both the rendered PDF and the stamp metadata. That makes it easy to save runtime-created libraries to your backend and later reload them with loadLibrary() or by publishing a manifest.

Live Example

The example below demonstrates both main workflows on one screen:

  • Browse the built-in stamp libraries and place a stamp.
  • Draw a square annotation, select it, save it into the custom library, and export that library as a PDF.
Loading PDF Engine...

Bring Your Own Stamp Library

Stamp libraries are intentionally simple:

  • Each stamp is one page in a PDF.
  • Each entry in stamps[] points at a pageIndex in that PDF.
  • Library-level categories apply to all stamps in the library.
  • Stamp-level categories let you filter or organize specific stamps differently.

Manifest Format

The manifest format is designed for publishing a library alongside a PDF file. Relative pdf paths are resolved relative to the manifest URL.

{ "id": "standard", "name": "Standard Stamps", "categories": ["sidebar"], "pdf": "stamps.pdf", "stamps": [ { "id": "approved", "pageIndex": 0, "name": "Approved", "subject": "Approved" }, { "id": "draft", "pageIndex": 1, "name": "Draft", "subject": "Draft" }, { "id": "confidential", "pageIndex": 2, "name": "Confidential", "subject": "Confidential" } ] }

Option A: Load Libraries From Manifests

Use manifests when you want to host a reusable library as static files:

createPluginRegistration(StampPluginPackage, { manifests: [ { url: '/stamps/en/manifest.json', fallbackLocale: 'en', }, ] })

You can also load the same thing later at runtime with stampCapability.loadLibraryFromManifest(url).

Option B: Preload Libraries Inline

Use libraries when you already have the PDF URL or ArrayBuffer and the metadata available in your app:

createPluginRegistration(StampPluginPackage, { libraries: [ { id: 'review', name: 'Review Stamps', pdf: '/stamps/review-stamps.pdf', readonly: true, categories: ['sidebar'], stamps: [ { id: 'approved', pageIndex: 0, name: PdfAnnotationName.Approved, subject: 'Approved', }, { id: 'rejected', pageIndex: 1, name: PdfAnnotationName.Rejected, subject: 'Rejected', }, ], }, ], })

You can load the same shape dynamically through the capability API:

stampCapability?.loadLibrary({ name: 'Uploaded Stamps', pdf: uploadedPdfBytes, stamps: uploadedStampDefinitions, })

Persist Runtime-Created Libraries

If users create their own libraries in the browser, a common pattern is:

  1. Let them build or edit stamps at runtime.
  2. Call exportLibrary() to get the PDF and stamps[] metadata.
  3. Save both to your backend.
  4. Reload the same library later with loadLibrary() or publish it as a manifest-backed library.

If you want to create an empty library first, use createNewLibrary(name, options) and then add stamp pages later with addStampToLibrary(libraryId, stamp, pdf).

API Reference

Configuration (StampPluginConfig)

OptionTypeDescription
librariesStampLibraryConfig[]Preloads one or more libraries from a PDF URL or ArrayBuffer plus stamp metadata.
manifestsStampManifestSource[]Loads one or more manifest-backed libraries. If the URL contains {locale}, the plugin replaces it with the current locale when the i18n plugin is available.
defaultLibraryDefaultLibraryConfig | falseConfigures the writable library used for runtime-created stamps. Set it to false to disable automatic default-library creation.

Hook: useStampCapability()

Connects your component to the global Stamp Plugin capability.

Returns

PropertyTypeDescription
providesStampCapability | nullThe stamp API object, or null until the plugin is ready.

Hook: useStampLibraries()

Keeps your component in sync with the currently loaded stamp libraries.

Returns

PropertyTypeDescription
librariesStampLibrary[]The loaded libraries, including manifest-backed and runtime-created libraries.
providesStampCapability | nullThe same capability returned by useStampCapability().

Hook: useStampsByLibrary(libraryId?, category?)

Returns a flattened array of stamps filtered by library and optional category.

ArgumentTypeDescription
libraryIdstring | undefinedThe library ID to filter by. Use 'all' or undefined to include every library.
categorystring | undefinedOptional category filter applied to both library-level and stamp-level categories.

Hook: useActiveStamp(documentId)

Returns the stamp currently armed for placement in the specified document.

Return TypeDescription
ActiveStampInfo | nullIncludes the libraryId and stamp being placed, or null if no stamp is active.

Component: <StampImg />

Renders a thumbnail of a stamp by rasterizing the matching page from its library.

PropTypeDescription
libraryIdstringRequired. The library that owns the stamp page.
pageIndexnumberRequired. The page index within the library PDF.
widthnumberRequired. The target thumbnail width in CSS pixels.
dprnumberOptional device pixel ratio override for sharper previews.

StampCapability Methods

MethodDescription
getLibraries()Returns every currently loaded library.
getStampsByCategory(category)Returns a flattened list of stamps whose library or individual stamp matches the category.
loadLibrary(config)Loads a library from a StampLibraryConfig.
loadLibraryFromManifest(url)Loads a library from a manifest URL at runtime.
createNewLibrary(name, options)Creates an empty library that can be populated later.
addStampToLibrary(libraryId, stamp, pdf)Adds a new one-page stamp PDF to an editable library.
exportLibrary(id)Exports a library as { name, pdf, stamps, categories }.
forDocument(documentId)Returns a document-scoped StampScope for placement and creation actions.

StampScope Methods

MethodDescription
activateStampPlacement(libraryId, stamp)Arms a specific stamp for placement in the current document.
activateStampPlacementById(libraryId, stampId)Looks up a stamp by ID, then arms it for placement.
createStampFromAnnotation(annotation, stamp, libraryId?)Turns one annotation into a reusable stamp in the target library.
createStampFromAnnotations(annotations, stamp, libraryId?)Creates one stamp from a group of annotations.
getActiveStamp()Returns the currently active stamp for this document, or null.
onActiveStampChangeEvent hook that fires when the active stamp changes for the document.
Last updated on March 30, 2026

Need Help?

Join our community for support, discussions, and to contribute to EmbedPDF's development.