react-canvas-draw and react-signature-canvas are both React components that enable drawing functionality on an HTML5 canvas element. react-canvas-draw is designed for freehand drawing applications, offering features like undo/redo support, customizable brush sizes, and the ability to save and load complex drawing data as JSON. react-signature-canvas is specialized for capturing user signatures, focusing on smooth bezier curves to mimic pen pressure, and typically exports signatures as image data URLs or point arrays for verification.
Both react-canvas-draw and react-signature-canvas bring HTML5 canvas capabilities to React applications, but they solve different problems. One is built for general-purpose drawing and annotation, while the other is fine-tuned for capturing legally binding or formal signatures. Let's look at how they handle rendering, data management, and customization.
react-canvas-draw focuses on replicating a natural drawing experience with support for multiple strokes. It renders every line as a distinct entity, which allows for complex interactions like selecting or modifying specific parts of the drawing later. It uses a standard 2D context to draw lines based on mouse or touch coordinates.
// react-canvas-draw: Basic setup for freehand drawing
import CanvasDraw from "react-canvas-draw";
function DrawingBoard() {
return (
<CanvasDraw
brushColor="#000000"
brushRadius={5}
canvasWidth={500}
canvasHeight={500}
/>
);
}
react-signature-canvas uses a bezier curve algorithm (often based on signature_pad) to smooth out jittery mouse movements. This makes signatures look like they were written with a real pen rather than a mouse. It treats the input as a single continuous path rather than distinct lines.
// react-signature-canvas: Basic setup for signatures
import SignatureCanvas from "react-signature-canvas";
function SignaturePad() {
return (
<SignatureCanvas
penColor="#000000"
canvasProps={{ width: 500, height: 500, className: "sigCanvas" }}
/>
);
}
react-canvas-draw saves the drawing state as a JSON object containing arrays of lines and points. This is lightweight and resolution-independent. You can load this JSON back into the canvas to let the user continue editing. This is critical for apps where the drawing is part of the workflow, not just a final artifact.
// react-canvas-draw: Saving and loading drawing state
import { useRef } from "react";
import CanvasDraw from "react-canvas-draw";
function App() {
const canvasRef = useRef(null);
const handleSave = () => {
const savedData = canvasRef.current.saveData(); // Returns JSON
console.log(savedData);
};
const handleLoad = () => {
const data = { lines: [...] }; // Your JSON data
canvasRef.current.loadData(data);
};
return (
<>
<CanvasDraw ref={canvasRef} />
<button onClick={handleSave}>Save</button>
<button onClick={handleLoad}>Load</button>
</>
);
}
react-signature-canvas is designed to export the final result as an image. The most common method is toDataURL(), which creates a base64 encoded PNG. This is what backend systems expect for document storage. It also supports toData() for point arrays, but the image export is the primary use case.
// react-signature-canvas: Exporting signature as image
import { useRef } from "react";
import SignatureCanvas from "react-signature-canvas";
function App() {
const sigRef = useRef(null);
const handleSave = () => {
// Returns base64 PNG data URL
const dataURL = sigRef.current.toDataURL("image/png");
console.log(dataURL);
};
const handleClear = () => {
sigRef.current.clear();
};
return (
<>
<SignatureCanvas ref={sigRef} />
<button onClick={handleSave}>Save Signature</button>
<button onClick={handleClear}>Clear</button>
</>
);
}
react-canvas-draw gives you control over the brush size and color. It allows for dynamic changes during the session. You can change the brush radius to simulate different pen tips or highlighters. It does not simulate pressure sensitivity by default but relies on consistent stroke width.
// react-canvas-draw: Dynamic brush customization
<CanvasDraw
brushColor="#ff0000"
brushRadius={10} // Thicker brush
lazyRadius={0} // Immediate rendering
hideGrid={true}
/>
react-signature-canvas simulates pen pressure by varying the line width based on the speed of the stroke. Faster movements create thinner lines, and slower movements create thicker lines. This is controlled via minWidth, maxWidth, and velocityFilterWeight.
// react-signature-canvas: Simulating pen pressure
<SignatureCanvas
penColor="#333"
minWidth={2} // Thinnest part of the stroke
maxWidth={5} // Thickest part of the stroke
velocityFilterWeight={0.5} // How much speed affects width
canvasProps={{ className: "sigCanvas" }}
/>
react-canvas-draw requires explicit width and height props. It does not automatically resize to fill a parent container. If you need a responsive canvas, you must calculate the dimensions in your component and pass them down. This can lead to layout shifts if not handled carefully.
// react-canvas-draw: Manual responsive handling
function ResponsiveDraw() {
const [dimensions, setDimensions] = useState({ w: 500, h: 500 });
useEffect(() => {
// Logic to calculate width based on window or parent
setDimensions({ w: window.innerWidth * 0.8, h: 400 });
}, []);
return <CanvasDraw canvasWidth={dimensions.w} canvasHeight={dimensions.h} />;
}
react-signature-canvas also relies on the underlying canvas element's width and height attributes. However, it is often wrapped in a container with CSS to handle scaling. The canvasProps prop allows you to pass standard HTML attributes, including class names for CSS styling, making it slightly easier to style via external stylesheets.
// react-signature-canvas: Styling via canvasProps
<SignatureCanvas
canvasProps={{
width: 500,
height: 200,
className: "signature-pad" // Target this class in CSS
}}
/>
react-canvas-draw provides a clear() method via the ref. This wipes the entire canvas state. Because it stores data as JSON lines, clearing it simply empties that array. There is no built-in "undo" button in the UI, but you can implement one by managing the saveData() stack yourself.
// react-canvas-draw: Clearing the board
const clearCanvas = () => {
if (canvasRef.current) {
canvasRef.current.clear();
}
};
react-signature-canvas also exposes a clear() method. Additionally, it offers isEmpty() to check if the user has drawn anything. This is crucial for form validation โ you don't want to submit an empty signature field.
// react-signature-canvas: Validation before submit
const handleSubmit = () => {
if (sigRef.current.isEmpty()) {
alert("Please provide a signature");
return;
}
// Proceed with submission
};
Despite their different goals, both libraries share common patterns because they rely on the same underlying HTML5 Canvas API.
useRef to access methods like clear() or save().// Shared pattern: Using refs for control
const ref = useRef(null);
// Access methods: ref.current.clear(), ref.current.save()
// Both work on mobile without extra setup
// <CanvasDraw /> or <SignatureCanvas />
// Supports touchstart, touchmove, touchend internally
// react-canvas-draw
<CanvasDraw brushColor="#4F46E5" />
// react-signature-canvas
<SignatureCanvas penColor="#4F46E5" />
// Both require manual implementation for undo
// Store previous states in an array: const [history, setHistory] = useState([])
// Import is straightforward for both
import CanvasDraw from "react-canvas-draw";
import SignatureCanvas from "react-signature-canvas";
| Feature | Shared by Both |
|---|---|
| Core Tech | ๐ผ๏ธ HTML5 Canvas API |
| Input | ๐ฑ๏ธ Mouse + Touch Support |
| Control | ๐๏ธ Ref-based Imperative Methods |
| Styling | ๐จ Custom Colors via Props |
| Integration | โ๏ธ Standard React Components |
| Feature | react-canvas-draw | react-signature-canvas |
|---|---|---|
| Primary Use | ๐จ Whiteboards, Annotations | โ๏ธ Signatures, Forms |
| Data Output | ๐ JSON (Lines/Points) | ๐ผ๏ธ Image (PNG/SVG Data URL) |
| Stroke Logic | ๐ Consistent Width | ๐ Variable Width (Pressure) |
| Editing | โ Load/Save State for Editing | โ Final Image Export Focus |
| Validation | โ No built-in empty check | โ
isEmpty() method |
react-canvas-draw is like a digital sketchbook ๐. It is built for scenarios where the drawing process matters, and the user might need to come back and change what they drew. Use this for collaborative whiteboards, image annotation tools, or creative apps.
react-signature-canvas is like a digital pen on a contract ๐. It is built for scenarios where the final result matters more than the process. It ensures the signature looks professional and exports in a format that legal and backend systems understand. Use this for checkout flows, onboarding forms, or approval workflows.
Final Thought: While both draw on a canvas, their data models dictate their use cases. If you need to edit the drawing later, choose the JSON-based approach of react-canvas-draw. If you need a final image for storage, choose the image-export focus of react-signature-canvas.
Choose react-canvas-draw if you are building a whiteboard, annotation tool, or creative drawing app where users need to draw multiple strokes, shapes, or freehand content. It is the better fit when you need to preserve the drawing state as editable vector-like data (lines and points) rather than a flat image, allowing for features like undo/redo or layer manipulation.
Choose react-signature-canvas if your primary goal is to capture a user's signature for forms, contracts, or authentication flows. It is optimized for single-stroke smoothness and provides built-in methods to export the result as a trimmed image (PNG/SVG) or data URL, which is standard for backend storage of signatures.
A simple yet powerful canvas-drawing component for React (Demo)
Install via NPM:
npm install react-canvas-draw --save
or YARN:
yarn add react-canvas-draw
import React from "react";
import ReactDOM from "react-dom";
import CanvasDraw from "react-canvas-draw";
ReactDOM.render(<CanvasDraw />, document.getElementById("root"));
For more examples, like saving and loading a drawing ==> look into the /demo/src folder.
These are the defaultProps of CanvasDraw. You can pass along any of these props to customize the CanvasDraw component. Examples of how to use the props are also shown in the /demo/src folder.
static defaultProps = {
onChange: null
loadTimeOffset: 5,
lazyRadius: 30,
brushRadius: 12,
brushColor: "#444",
catenaryColor: "#0a0302",
gridColor: "rgba(150,150,150,0.17)",
hideGrid: false,
canvasWidth: 400,
canvasHeight: 400,
disabled: false,
imgSrc: "",
saveData: null,
immediateLoading: false,
hideInterface: false,
gridSizeX: 25,
gridSizeY: 25,
gridLineWidth: 0.5,
hideGridX: false,
hideGridY: false
enablePanAndZoom: false,
mouseZoomFactor: 0.01,
zoomExtents: { min: 0.33, max: 3 },
};
Useful functions that you can call, e.g. when having a reference to this component:
getSaveData() returns the drawing's save-data as a stringified objectloadSaveData(saveData: String, immediate: Boolean) loads a previously saved drawing using the saveData string, as well as an optional boolean flag to load it immediately, instead of live-drawing it.getDataURL(fileType, useBgImage, backgroundColour) will export the canvas to a data URL, which can subsequently be used to share or manipulate the image file.clear() clears the canvas completely, including previously erased lines, and resets the view. After a clear, undo() will have no effect.eraseAll() clears the drawn lines but retains their data; calling undo() can restore the erased lines. Note: erased lines are not included in the save data.resetView() resets the canvas' view to defaults. Has no effect if the enablePanAndZoom property is false.undo() removes the latest change to the drawing. This includes everything drawn since the last MouseDown event.This repo was kickstarted by nwb's awesome react-component starter.
You just need to clone it, yarn it & start it!
If you want to save large strings, like the stringified JSON of a drawing, I recommend you use pieroxy/lz-string for compression. It's LZ compression will bring down your long strings to only ~10% of its original size.
The lazy-brush project as well as its demo app by dulnan have been a heavy influence.
I borrowed a lot of the logic and actually used lazy-brush during the push to v1 of react-canvas-draw. Without it, react-canvas-draw would most likely still be pre v1 and wouldn't feel as good.
Thanks goes to these wonderful people (emoji key):
Martin Beierling-Mutz ๐ป ๐ ๐ก ๐ค | Jan Hug ๐ค |
|---|
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT, see LICENSE for details.