SignaturePad
ComponentsSignature captureElectronic signature pad for React — hand-written ink engine, velocity/pressure-based variable stroke width, PNG/SVG/JSON exports, zero dependencies
Live demo
Install
$ npm install @usefy/signature-padQuick start
import { useRef, useState } from "react";
import { SignaturePad, type SignaturePadController } from "@usefy/signature-pad";
function ConsentForm() {
const pad = useRef<SignaturePadController>(null);
const [signed, setSigned] = useState(false);
return (
<>
<div style={{ height: 200, border: "1px solid #e2e8f0", borderRadius: 12 }}>
<SignaturePad
guideline
controllerRef={pad}
onChange={({ isEmpty }) => setSigned(!isEmpty)}
/>
</div>
<button
disabled={!signed}
onClick={async () => {
const { blob } = await pad.current!.toPNG({ background: "#fff" });
await upload(blob);
}}
>
Agree & sign
</button>
</>
);
}API reference
<SignaturePad />
<SignaturePad
penColor="#1e293b"
guideline={{ y: 140, inset: 24, color: "#94a3b8" }}
defaultValue={storedSignature} // applied once on mount
readOnly={false} // reactive — input detaches in place
controllerRef={pad} // imperative controller (stable)
onBegin={() => {}}
onEnd={() => {}}
onChange={({ isEmpty, strokeCount }) => {}} // edges only, never per point
aria-label="Signature input area"
className="..."
style={{ ... }}
/>
controllerRefreceives a stableSignaturePadController:clear() / undo() / redo() / isEmpty() / toPNG() / toSVG() / toJSON() / fromJSON(). Calls before mount (or after unmount) are safe no-ops.guidelinedraws a "sign here" baseline beneath the ink, on-canvas — and it is excluded from every export by construction.- Ink props are live: changing
penColor(etc.) never wipes the pad — committed strokes keep the style they were drawn with; new strokes pick up the new options. (History resets, and a stroke in progress at that exact moment is discarded.)
useSignaturePad()
import { useSignaturePad } from "@usefy/signature-pad";
function SignatureField() {
const pad = useSignaturePad({ penColor: "#0f172a" });
return (
<>
<canvas ref={pad.canvasRef} style={{ width: "100%", height: 200 }} />
<button onClick={pad.undo} disabled={!pad.canUndo}>Undo</button>
<button onClick={pad.redo} disabled={!pad.canRedo}>Redo</button>
<button onClick={pad.clear} disabled={pad.isEmpty}>Clear</button>
</>
);
}
Returns { canvasRef, isEmpty, strokeCount, canUndo, canRedo, clear, undo, redo, toPNG, toSVG, toJSON, fromJSON } — every function referentially stable. The engine is created when the canvas attaches and destroyed when it detaches; options are read once at attach (remount or attach a new canvas to change them).
Go deeper
This page is the quick reference. For every example, prop, and edge case, read the full README — or open Storybook to change props live.