PDF Signature

Draw, type, or upload a signature, keep a reusable library, and click a page to place one.

"use client";

import { createPluginRegistration } from "@embedpdf/core";

About

The front end for embedpdf's signature plugin, which owns the whole workflow — capturing a signature, keeping a reusable library of them, and rendering a placed one through the annotation layer. This ships as one component with its parts exported:

  • <PdfSignatureCreator /> — draw, type, or upload, built on embedpdf's own headless pads: <SignatureDrawPad />, <SignatureTypePad />, and the useSignatureUpload hook. Saving adds the result to the library via the signature capability's addEntry.
  • <PdfSignatureLibrary /> — the saved signatures. Picking one arms placement (forDocument(documentId).activateSignaturePlacement(entryId)); the next click on a page drops it in.
  • <PdfSignatureManager /> — the two stacked, with no container of its own.
  • <PdfSignatureDialog /> — the manager in a modal, because the next click belongs to the page.
  • <PdfSignatureButton /> — 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 — a settings page can render the creator with no placement flow around it.

Placed signatures render through the annotation plugin's <PdfAnnotationLayer> — the signature plugin registers its renderers there — so both plugins have to be registered and a layer rendered.

Every surface that holds a signature — the two pads, each card in the saved library — is paper-coloured in both schemes. The plugin bakes the ink colour into what it hands back, and that ink is black so it survives being stamped on a page, so the surface under it has to stay white too. They all read --pdf-page; see Recolouring the paper. The upload dropzone is the exception until an image lands on it — an empty dropzone is app chrome, not a signature.

Installation

pnpm dlx shadcn@latest add @pdfviewcn/signature

Usage

Register the annotation and signature plugins.

import { createPluginRegistration } from "@embedpdf/core";
import { AnnotationPluginPackage } from "@embedpdf/plugin-annotation/react";
import { SignaturePluginPackage } from "@embedpdf/plugin-signature/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(SignaturePluginPackage),
];

Render the annotation layer — placed signatures render through it — and drop the button in the toolbar.

<PdfViewer
  documents={[{ url: "/sample.pdf" }]}
  plugins={plugins}
  className="h-[720px]"
>
  <PdfToolbar>
    <PdfSignatureButton />
  </PdfToolbar>
  <PdfViewerContent
    pageLayers={({ documentId, pageIndex }) => (
      <PdfAnnotationLayer documentId={documentId} pageIndex={pageIndex} />
    )}
  />
</PdfViewer>

Examples

Default

Open the button, create a signature by drawing, typing, or uploading an image, and save it. Pick a saved signature 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

<PdfSignatureDialog /> takes a trigger through children, so the toolbar toggle isn't the only way in.

<PdfSignatureDialog>
  <DialogTrigger render={<Button variant="outline" />}>
    Add signature
  </DialogTrigger>
</PdfSignatureDialog>

Your own container

The dialog is a convenience, not a requirement. Mount <PdfSignatureManager /> in a popover, a sheet, or a sidebar panel instead — it brings no container of its own.

<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>
    Signature
  </PopoverTrigger>
  <PopoverContent className="w-80">
    <PdfSignatureManager />
  </PopoverContent>
</Popover>

Creator on its own

A settings page has no page to click, so it wants the creator without the library or the placement flow. Saving still writes to the plugin's library, so signatures made here show up in the viewer later.

<PdfSignatureCreator defaultValue="type" />

Accessibility

The trigger is a single toggle button labelled "Signature", 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 signature — or clicking the button cancels.

Placing a signature requires a pointer, since the drop point is chosen by clicking a page; there's no keyboard equivalent for picking a spot. Creation is keyboard reachable: the upload dropzone is a real button, so it takes focus and opens the file picker on Enter or Space, and each saved signature is a button named by its label.

API Reference

PdfSignatureButton

A <Toggle /> carrying data-slot="pdf-signature-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.

PropTypeDefaultDescription
openbooleanWhether the signature dialog is open.
defaultOpenbooleanfalseInitial open state. Uncontrolled.
onOpenChange(open: boolean, eventDetails?) => voidCalled when the dialog opens or closes.

The button reads the signature capability through useSignatureCapability() and the armed placement through useActivePlacement(documentId). It renders disabled whenever there's no active document or the signature plugin isn't registered.

PdfSignatureDialog

<PdfSignatureManager /> in a modal, because the next click belongs to the page rather than to the dialog. Picking a signature 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.

PdfSignatureManager

<PdfSignatureLibrary /> stacked over <PdfSignatureCreator /> in a plain <div> carrying data-slot="pdf-signature-manager". Forwards onSelect and onSave to its two parts.

PdfSignatureCreator

The draw/type/upload tabs, carrying data-slot="pdf-signature-creator". Accepts every <Tabs /> prop, so defaultValue picks the starting tab.

PropTypeDefaultDescription
onSave(definition: SignatureFieldDefinition) => voidReplaces the default, which saves the signature to the plugin's library.

The Type tab's field isn't a preview of the result. The pad renders the artifact on an offscreen canvas at 48px, centred and cropped to the ink, and shows you a plain text input capped at 32px — the two aren't decoupled upstream.

PdfSignatureLibrary

The saved signatures as a pick-to-place grid, carrying data-slot="pdf-signature-library". Renders nothing until there's at least one saved signature.

PropTypeDefaultDescription
onSelect(entryId: string) => voidReplaces the default, which arms placement for the picked signature.
onRemove(entryId: string) => voidReplaces the default, which deletes the entry from the plugin's library.