React Signature Pad: Capture Signatures in React with TypeScript
Updated · 9 min read
Collecting a signature is a common requirement in delivery apps, rental agreements, onboarding flows, and consent forms. In React you need a canvas that reacts to mouse, touch, and stylus input, a way to undo mistakes, a check that the user actually signed, and an export your backend can store. This guide walks through all of that with AutographJS, a lightweight TypeScript-first signature library with a native React component.
What you will build
- A responsive signature pad component that works with mouse, touch, and pen input.
- Undo, redo, and clear controls.
- Validation that blocks empty signatures before a form is submitted.
- Export to PNG, JPEG, or SVG, plus a download button.
- An upload to your API and a minimal Express endpoint that saves the image.
If you want to see the result before writing code, open the interactive signature demo and try the brushes and export formats.
1. Install the package
npm install autographjsThe package ships its own TypeScript definitions, so you do not need a separate @types package. The React component lives in the autographjs/react entry point, and the framework-agnostic core and helpers live in autographjs.
2. Add a basic React signature pad
ReactSignaturePad renders a canvas and exposes its methods through a ref typed as SignaturePadRef:
import { useRef } from "react";
import { ReactSignaturePad } from "autographjs/react";
import type { SignaturePadRef } from "autographjs/react";
export function SignatureField() {
const padRef = useRef<SignaturePadRef>(null);
return (
<ReactSignaturePad
ref={padRef}
width={600}
height={200}
strokeColor="#1a1a2e"
strokeWidth={2.5}
backgroundColor="#ffffff"
/>
);
}The width and height props set the drawing resolution (600 by 200 by default). On screen the canvas is styled to fill its container up to that width while keeping the aspect ratio, and pointer coordinates are scaled to match, so the same component works on a phone and on a desktop. For sharper output on high-density screens, increase both values proportionally, for example width={1200} height={400}, and constrain the wrapper with CSS.
Other props include brushType ("pen", "marker", "calligraphy" or "dotted"), className, and onChange, which fires after each finished stroke and after clear, undo, or redo. The full list is in the AutographJS documentation.
3. Undo, redo, and clear
People rarely sign perfectly on the first try, especially on a touchscreen. The ref gives you stroke-level history, so undo removes the last stroke instead of wiping the whole signature:
export function SignatureField() {
const padRef = useRef<SignaturePadRef>(null);
return (
<div>
<ReactSignaturePad ref={padRef} />
<div className="actions">
<button type="button" onClick={() => padRef.current?.undo()}>Undo</button>
<button type="button" onClick={() => padRef.current?.redo()}>Redo</button>
<button type="button" onClick={() => padRef.current?.clear()}>Clear</button>
</div>
</div>
);
}Use type="button" on these buttons when the pad sits inside a form, otherwise every click submits the form.
4. Validate that the user actually signed
An empty canvas still exports a valid image, so always check before you submit. isEmpty() reports whether there are any strokes, and getStrokeCount() lets you reject an accidental tap. Read these values when the user submits rather than caching them in state, so they always reflect the latest strokes:
export function SignatureForm() {
const padRef = useRef<SignaturePadRef>(null);
const [error, setError] = useState("");
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
const pad = padRef.current;
if (!pad || pad.isEmpty()) {
setError("Please sign before submitting.");
return;
}
// Optional: reject a single accidental tap or dash
if (pad.getStrokeCount() < 2) {
setError("Your signature looks too short. Please sign again.");
return;
}
setError("");
const png = pad.toPNG(); // "data:image/png;base64,..."
// ...submit png together with the rest of the form
};
return (
<form onSubmit={handleSubmit}>
<ReactSignaturePad ref={padRef} />
{error && <p role="alert">{error}</p>}
<button type="submit">Sign and continue</button>
</form>
);
}Show the error next to the pad with role="alert" so screen reader users hear it, and keep a visible label such as "Sign here" above the canvas.
5. Export the signature as PNG, JPEG, or SVG
const pad = padRef.current;
pad?.toPNG(); // PNG data URL, best default for documents
pad?.toJPEG(); // JPEG data URL, smaller, no transparency
pad?.toSVG(); // SVG markup string, scales without blur
pad?.toBase64("image/webp"); // any type the browser's canvas supportsPNG is the safest default for contracts and PDFs. JPEG makes smaller files but cannot be transparent, so keep a white backgroundColor if you use it. SVG stores the strokes as vector paths, which is useful when the signature is printed or scaled. To pass a transparent PNG into a PDF generator, set backgroundColor="transparent".
To let users save a copy, turn the data URL into a download:
function downloadSignature(pad: SignaturePadRef) {
const link = document.createElement("a");
link.href = pad.toPNG();
link.download = "signature.png";
link.click();
}6. Send the signature to your backend
You can post toPNG() with fetch yourself, or use the sendToAPI helper, which picks the export format, adds a timestamp, and merges any metadata into a JSON body:
import { sendToAPI } from "autographjs";
async function saveSignature(pad: SignaturePadRef, documentId: string) {
const response = await sendToAPI(pad, {
url: "/api/signatures",
format: "png",
headers: { Authorization: `Bearer ${token}` },
metadata: { documentId },
});
if (!response.ok) throw new Error("Could not save the signature");
}
// Request body sent by sendToAPI:
// { "signature": "data:image/png;base64,...", "format": "png",
// "timestamp": "2026-10-20T12:00:00.000Z", "documentId": "..." }On the server, strip the data URL prefix and decode the base64 payload. A minimal Express endpoint looks like this:
import express from "express";
import { writeFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";
const app = express();
app.use(express.json({ limit: "2mb" }));
app.post("/api/signatures", async (req, res) => {
const { signature, documentId } = req.body;
const match = /^data:image\/png;base64,(.+)$/.exec(signature ?? "");
if (!match) return res.status(400).json({ error: "Expected a PNG data URL" });
const file = `signatures/${randomUUID()}.png`;
await writeFile(file, Buffer.from(match[1], "base64"));
// store file path + documentId + timestamp in your database here
res.status(201).json({ file });
});
app.listen(3000);In production, store the file in object storage, record who signed, when, and which document version they saw, and serve the image back only to authorised users. A Python / Flask version of this endpoint is in the backend integration section of the docs.
7. Using the signature pad with Next.js
The signature pad draws on a browser canvas and uses React hooks, so in the Next.js App Router it must run in a Client Component. Put it in its own file with the "use client" directive and import that file from your server page:
"use client";
import { ReactSignaturePad } from "autographjs/react";
export default function SignaturePadClient() {
return <ReactSignaturePad />;
}8. AutographJS vs react-signature-canvas
react-signature-canvas is the most downloaded option and is a React wrapper around the signature_pad library. It is a solid choice if you already use it. AutographJS takes a different approach: the React component is native rather than a wrapper, and the same package also ships Vue and Vanilla JS versions. The main practical differences:
| Need | AutographJS | react-signature-canvas |
|---|---|---|
| Undo and redo | Built in (undo(), redo()) | Manual, via toData() and fromData() |
| Brush styles | Pen, marker, calligraphy, dotted | One pen with width and color options |
| Upload helper | sendToAPI() | Write your own request |
| Other frameworks | Same package for React, Vue, Vanilla JS | React only |
The AutographJS home page has a wider comparison that also covers signature_pad and jSignature.
Production checklist
- Validate with
isEmpty()on submit, and on the server reject empty or oversized payloads. - Give the pad a visible label and a clear button large enough to tap.
- Keep a white background for JPEG, or transparent PNG when the signature is placed on a document.
- Store the signer, timestamp, and document version next to the image for an audit trail.
- Serve signature images only to authorised users; they are personal data.
FAQ
How do I check if the signature pad is empty in React?
Call padRef.current?.isEmpty() when the user submits. It returns true when no strokes have been drawn.
How do I save a React signature as an image?
Call toPNG() or toJPEG() to get a data URL, then post it to your API or assign it to a link's href with a download attribute.
Does it work on mobile and with a stylus?
Yes. The component uses Pointer Events, so mouse, touch, and pen input all work, and stylus pressure is recorded for each point.
Can I use the same signature pad in Vue?
Yes. Import SignaturePad from autographjs/vue; the options and export methods match the React version. See the docs for the Vue and Vanilla JS examples.
Next steps
Try every option in the live demo, read the full API in the documentation, or install the package from npm and star the project on GitHub.