SignatureKit
Recipes

React components

Install SignatureKit's shadcn registry components into your app and own the UI code.

@signature-kit/react is hooks-only. Install UI with the shadcn registry when you want a ready path; the generated files land in your app, so storage, copy, styling, fetch/tRPC, and product behavior stay yours.

Install one component
npx shadcn@latest add https://signaturekit.dev/r/signature-dialog.json

Registry items

ItemCommandUse it for
signature-certificate-formnpx shadcn@latest add https://signaturekit.dev/r/signature-certificate-form.jsonPassword confirmation with callback-owned getSavedPassword / onSavePassword.
certificate-upload-formnpx shadcn@latest add https://signaturekit.dev/r/certificate-upload-form.jsonPick .pfx / .p12, parse it in the browser with useA1Certificate, preview metadata, then call your upload callback.
signature-pdf-viewernpx shadcn@latest add https://signaturekit.dev/r/signature-pdf-viewer.jsonreact-pdf preview with text-anchor highlighting and manual click-to-place rectangles.
signature-dialognpx shadcn@latest add https://signaturekit.dev/r/signature-dialog.jsonDirect A1 signing flow around useA1Signer, saved-password retry, progress, and signed-PDF download preview.
signing-progress-listnpx shadcn@latest add https://signaturekit.dev/r/signing-progress-list.jsonStandalone per-document status rows for a custom signing screen.
pdf-signature-anchornpx shadcn@latest add https://signaturekit.dev/r/pdf-signature-anchor.jsonInvisible @react-pdf/renderer text token paired with findPdfTextAnchors.

Direct signing dialog

sign-button.tsx
import { SignatureDialog } from "@/components/signature-dialog"

export function SignButton({ pfx }: { pfx: Uint8Array }) {
  return (
    <SignatureDialog
      pfx={pfx}
      anchorTokens={["{{signature}}"]}
      getSavedPassword={() => null}
      onWrongPassword={() => {
        // Clear your app-owned password storage here.
      }}
      buildDocuments={async () => [
        {
          id: "contract",
          name: "contract.pdf",
          pdf: await fetch("/contract.pdf").then(async (response) =>
            new Uint8Array(await response.arrayBuffer()),
          ),
          anchors: {
            matchers: [{ text: "{{signature}}" }],
            stampSize: { width: 180, height: 54 },
          },
        },
      ]}
      signing={{ policy: "pades-icp-brasil", reason: "Signed with SignatureKit" }}
      stamp={{
        badge: {
          header: { text: "DIGITALLY SIGNED" },
          rows: [[{ label: "Signer", value: "Current user" }]],
          footer: [{ text: "ICP-Brasil" }, { text: "SignatureKit" }],
        },
        rubric: { initials: "CU" },
      }}
      onSigned={(rows) => {
        // Upload, preview, or download rows[n].signedPdf.
      }}
    />
  )
}

Anchor-first placement

Use invisible text anchors when you generate PDFs with @react-pdf/renderer. This keeps placement deterministic and avoids asking a user to click every document.

contract.pdf.tsx
import { Text, View } from "@react-pdf/renderer"
import { PdfSignatureAnchor } from "@/components/pdf-signature-anchor"

export function ContractSignatureBlock() {
  return (
    <View>
      <Text>Signer</Text>
      <PdfSignatureAnchor token="{{signature}}" />
    </View>
  )
}

Use signature-pdf-viewer when a human needs to confirm anchor results or switch to manual click-to-place.

Waku/FumaPress react-pdf island

react-pdf touches browser APIs, so keep it behind the hydration boundary shown in PdfPreviewIsland. Use useSyncExternalStore to expose a server snapshot of false, lazy to defer the browser-only module, and Suspense for the loading fallback. Waku/FumaPress pages can pass validated PDF bytes, pages, and rectangles into that island; it owns preview state and object URLs without a framework-specific client-loading helper.

How is this guide?

On this page