Inputs
SignaturePad
Canvas-based signature capture with smooth freehand strokes, DPR-correct rendering, and export to a PNG data URL.
SignaturePadPreview
Usage
tsx
import { useRef } from "react";
import { SignaturePad } from "@vesture/react";
import type { SignaturePadHandle } from "@vesture/react";
function Example() {
const padRef = useRef<SignaturePadHandle>(null);
return (
<>
<SignaturePad
aria-label="Sign here"
penColor="#1d4ed8"
onChange={(dataUrl) => console.log(dataUrl)}
/>
<button onClick={() => padRef.current?.clear()}>Clear</button>
</>
);
}Behavior
- Draw with mouse, touch, or pen — built on Pointer Events, one code path for all input types. Strokes are smoothed via quadratic curve interpolation between points rather than straight segments between raw samples, so freehand signatures look natural instead of jagged.
- onChange fires on every stroke completion (pointerup) with a PNG data URL, and with null when cleared — there's no separate "submit" step, each stroke is a complete, exportable state.
- A single stationary tap (no drag) still registers as a small dot and counts as content, matching how physical signature pads behave.
- Canvas is sized in real device pixels (accounting for window.devicePixelRatio) and scaled back down in CSS, so the pad renders crisp on high-DPI displays instead of blurry.
- Imperative ref handle — clear(), toDataURL(type?), isEmpty() — for building your own controls instead of the built-in Clear button (showClearButton={false}), or for pulling the signature out on your own form-submit timing rather than via onChange.
- penColor/penWidth/backgroundColor are read fresh at the start of each new stroke, so changing them mid-session doesn't retroactively repaint or wipe strokes already drawn — only width/height changes reset the canvas, since resizing a <canvas> element is inherently destructive to its bitmap.
Props
SignaturePad
| Prop | Type | Default | Description |
|---|---|---|---|
| onChange | (dataUrl: string | null) => void | — | Fires on every stroke completion with a PNG data URL, or null when cleared. |
| penColor | string | vars.color.text | Any valid CSS color. |
| penWidth | number | 2.5 | Stroke width in CSS pixels. |
| backgroundColor | string | transparent | Composites onto whatever surface the pad sits on when left transparent. |
| width / height | number | 400 / 160 | Canvas size in CSS pixels. Changing either resets the canvas (unavoidable — canvas resizing always clears the bitmap). |
| disabled | boolean | — | Blocks new strokes; existing content and the Clear button (also disabled) are unaffected visually. |
| showClearButton | boolean | true | Set false to build your own controls against the ref instead. |
| aria-label | string | "Signature pad" | No visible label exists on the canvas itself, so this names it for assistive tech. |
SignaturePadHandle (ref)
| Prop | Type | Default | Description |
|---|---|---|---|
| clear() | () => void | — | Wipes the canvas and fires onChange(null). |
| toDataURL(type?) | (type?: string) => string | — | Returns the current signature as a data URL. type defaults to "image/png". |
| isEmpty() | () => boolean | — | True if nothing has been drawn (and it hasn't been cleared since). |