A sketch field component for React applications, built on fabric.js. Draw with a pencil, lines, arrows, rectangles and circles, select and move objects, add images and text, undo and redo, and export the drawing as JSON or as an image. The package includes TypeScript declarations.
yarn add react-sketchor
npm install react-sketchreact (^17.0.0 || ^18.0.0 || ^19.0.0) is a peer dependency: install it in your application.
fabric is a dependency of react-sketch and is installed with it.
SketchField is a class component. The methods of the component (undo, toJSON, ...) are available
through a ref, from the moment the component is mounted until it is unmounted.
import { useRef, useState } from "react";
import { SketchField, Tools } from "react-sketch";
export const Drawing = () => {
const sketch = useRef<SketchField>(null);
const [canUndo, setCanUndo] = useState(false);
return (
<>
<SketchField
ref={sketch}
width={1024}
height={768}
tool={Tools.Pencil}
lineColor="black"
lineWidth={3}
onChange={() => setCanUndo(sketch.current?.canUndo() ?? false)}
/>
<button disabled={!canUndo} onClick={() => sketch.current?.undo()}>
Undo
</button>
</>
);
};All types are available in the Types namespace, for example Types.SketchProperties,
Types.SketchEvent and the Types.Tool enumeration:
import { SketchField, Types } from "react-sketch";
const onObjectAdded = (event: Types.SketchEvent) => {
console.log("added a", event.target?.type);
};
export const Highlight = () => (
<SketchField
tool={Types.Tool.HIGHLIGHTER}
lineColor="#ffcc00"
onObjectAdded={onObjectAdded}
/>
);import { useRef } from "react";
import { SketchField, Tools } from "react-sketch";
export const Drawing = () => {
const sketch = useRef(null);
return (
<div>
<SketchField
ref={sketch}
width={1024}
height={768}
tool={Tools.Pencil}
lineColor="black"
lineWidth={3}
/>
<button onClick={() => sketch.current.clear()}>Clear</button>
</div>
);
};The default export of 0.5.x keeps working. It is the component, which also carries SketchField and
Tools:
import Sketch from "react-sketch";
export const Drawing = () => <Sketch tool={Sketch.Tools.Pencil} lineWidth={3} />;toJSON() returns the drawing as an object. defaultValue and value accept that object, or the same
object as a JSON string. The object has the format of fabric 7 (object types such as "Rect" and
"IText"). Drawings saved with react-sketch 0.5.x load as well, drawings saved with 1.0.0 do not load
in 0.5.x.
defaultValueis loaded when the component mounts, unlessvalueis set.valueis loaded when it changes (a different object, compared by identity). Pass a new object, for example{ ...saved }, to load the same drawing again, or setforceValueto loadvalueon every update of the component.- The drawing is loaded 100 ms later, and
onChangeis called when the load is done. The undo history starts again with the loaded objects, so each of them can be undone, andonObjectAddedis called for each. - A value which cannot be loaded (not valid JSON, or an object type which is not known) leaves the canvas and its undo history as they were, and the error is thrown as an uncaught error: it is not hidden. A newer value cancels the load of an older one which is still running, so the last value is the one shown.
valueis not updated while the user draws. Read the drawing withtoJSON(). Do not store the result oftoJSON()invaluefromonChange: loading a value callsonChange, so the value would be loaded again and again.- Images added with
addImgand the background image are part of the JSON, so onevalueordefaultValueholds shapes and images. Loading replaces everything, the background image included: add images afteronChangereports the load.
import { useRef, useState } from "react";
import { SketchField, Tools, Types } from "react-sketch";
export const SaveAndLoad = ({ initial }: { initial: Types.SketchValue }) => {
const sketch = useRef<SketchField>(null);
const [saved, setSaved] = useState<Types.SketchData>();
const [value, setValue] = useState<Types.SketchValue>();
return (
<>
<SketchField
ref={sketch}
tool={Tools.Pencil}
defaultValue={initial}
value={value}
/>
<button onClick={() => setSaved(sketch.current?.toJSON())}>Save</button>
<button disabled={!saved} onClick={() => setValue(saved && { ...saved })}>
Load
</button>
</>
);
};import { useRef } from "react";
import { SketchField, Tools } from "react-sketch";
export const Editor = ({ imageUrl }: { imageUrl: string }) => {
const sketch = useRef<SketchField>(null);
const download = () => {
const link = document.createElement("a");
link.href = sketch.current?.toDataURL({ format: "png", multiplier: 2 }) ?? "";
link.download = "sketch.png";
link.click();
};
return (
<>
<SketchField ref={sketch} tool={Tools.Select} backgroundColor="white" />
<button onClick={() => sketch.current?.addImg(imageUrl, { scale: 0.25 })}>
Add image
</button>
<button onClick={() => sketch.current?.addText("Hello", { fontSize: 24 })}>
Add text
</button>
<button
onClick={() =>
sketch.current?.setBackgroundFromDataUrl(imageUrl, { stretched: true })
}>
Background
</button>
<button onClick={download}>Download</button>
</>
);
};All properties are optional.
| Property | Type | Default | Description |
|---|---|---|---|
tool |
Types.Tool or string |
"select" |
The tool to use, see Tools. null is treated as "select", an unknown name selects "pencil". |
lineColor |
string |
"black" |
Color of the line. |
lineWidth |
number |
10 |
Width of the line. |
fillColor |
string |
"transparent" |
Fill color of the shapes, where applicable (rectangle, rectangle-label, circle). |
backgroundColor |
string |
"transparent" |
Background color of the sketch, in hex or rgba format. |
opacity |
number |
1.0 |
Accepted and not applied: it has no effect. It had no effect in 0.5.x either. |
undoSteps |
number |
25 |
Number of undo/redo steps to keep. Read when the component mounts, a later change has no effect. |
imageFormat |
string |
Default format of toDataURL, png or jpeg, used when the options of the call have no format. Without it, png. |
|
value |
object or string |
Drawing to load, loaded again when it changes. See Initial, loaded and saved drawings. | |
forceValue |
boolean |
false |
Load value on every update of the component, even when it did not change. |
defaultValue |
object or string |
Drawing loaded when the component mounts, unless value is set. |
|
widthCorrection |
number |
0 |
Pixels subtracted from the width of the container when the canvas is resized. Compensates a border or padding of the container. |
heightCorrection |
number |
0 |
Pixels subtracted from the height of the container when the canvas is resized. |
autoresize |
boolean |
true |
Resize the canvas, and scale its objects, when the window is resized. The canvas is always fitted to its container on mount and when width or height change. |
width |
number |
undefined | Width of the sketch in pixels. When not set, the sketch takes the width of its parent element. |
height |
number |
512 |
Height of the sketch in pixels. |
className |
string |
Class name of the container element of the canvas. | |
style |
CSS.Properties |
Style of the container element of the canvas. | |
onChange |
(event?: Event) => void |
Called 10 ms after every mouse up and mouse out on the canvas, whether or not the drawing changed (see Known limitations), and after undo, redo and the load of a value. The argument is the DOM event after a mouse event, nothing otherwise. |
|
onObjectAdded |
(event: Types.SketchEvent) => void |
no-op | An object was added: drawn, pasted, or loaded with a value. Not called when undo or redo add an object back. |
onObjectModified |
(event: Types.SketchEvent) => void |
no-op | An object was modified by the user (moved, scaled, rotated). |
onObjectRemoved |
(event: Types.SketchEvent) => void |
no-op | An object was removed from the sketch. |
onObjectMoving |
(event: Types.SketchEvent) => void |
no-op | An object is being moved. |
onObjectScaling |
(event: Types.SketchEvent) => void |
no-op | An object is being scaled. |
onObjectRotating |
(event: Types.SketchEvent) => void |
no-op | An object is being rotated. |
onMouseDown |
(event: Types.SketchEvent) => void |
no-op | Mouse down on the canvas. |
onMouseMove |
(event: Types.SketchEvent) => void |
no-op | Mouse move on the canvas. |
onMouseUp |
(event: Types.SketchEvent) => void |
no-op | Mouse up on the canvas. |
onMouseOut |
(event: Types.SketchEvent) => void |
no-op | The mouse left the canvas. |
Types.SketchEvent is { e?: Event; target?: Types.SketchObject; [key: string]: unknown }: e is the
DOM event, target is the object the event is about (object events only). Types.SketchObject is
{ type?: string; [key: string]: unknown }, for example with the type "path", "line" or "rect".
The methods are properties of the component instance, access them through a ref. They are valid from
the moment the component is mounted until it is unmounted. Calling one before the component is mounted
throws.
| Method | Returns | Description |
|---|---|---|
undo() |
void |
Undo the last action. Does nothing when there is nothing to undo. Calls onChange when it undid something. |
redo() |
void |
Redo the last undone action. Does nothing when there is nothing to redo. Calls onChange when it redid something. |
canUndo() |
boolean |
Whether undo() has something to undo. |
canRedo() |
boolean |
Whether redo() has something to redo. |
toDataURL(options?) |
string |
The sketch as a data URL. Options: format ("png" or "jpeg", default imageFormat, then "png"), quality (0 to 1, jpeg only, default 1), multiplier (default 1), and the crop area left, top, width, height. |
toJSON(propertiesToInclude?) |
Types.SketchData |
The sketch as an object. propertiesToInclude lists additional properties of the objects to include. |
fromJSON(json?) |
void |
Load a drawing (object or JSON string). Loads after 100 ms, starts the undo history again with the loaded objects, applies the tool configuration again and calls onChange. Does nothing for an empty argument. |
clear(propertiesToInclude?) |
Types.SketchData |
Remove all objects and the background image, and reset the undo history. Returns the discarded content as JSON. The backgroundColor property is kept. |
addImg(dataUrl, options?) |
void |
Add an image, given as URL or data URL, as an object. It is added when it has loaded. Options: left, top (default: random position), scale (default 0.5). |
addText(text, options?) |
void |
Add an editable text. It is centered unless left or top are given. options are the options of a fabric text object, for example fontSize, fill. |
setBackgroundFromDataUrl(dataUrl, options?) |
void |
Use an image as background. Options: stretched (fill the canvas, the proportions change), stretchedX (fit the width), stretchedY (fit the height), both together are stretched; any other option is an image option. |
removeBackgroundImage() |
void |
Remove the background image, also one that is still loading. The objects and the backgroundColor are kept. Like setBackgroundFromDataUrl, it is not part of the undo history. |
zoom(factor) |
void |
Scale the objects and the background image by a factor: 2 doubles their size, 0.5 halves it. |
hasSelection() |
boolean |
Whether an object is selected. |
clearSelection() |
void |
Deselect the selected object or objects. |
removeSelected() |
void |
Remove the selected object or objects. It can be undone. |
copy() |
void |
Copy the selected object. Does nothing without a selection. |
paste() |
void |
Paste the copied object, 10 pixels further down and to the right of the previous one. Does nothing when nothing was copied. |
enableTouchScroll() |
void |
Allow the page to scroll on touch devices when touching the canvas. |
disableTouchScroll() |
void |
Do not scroll the page when touching the canvas. |
Use the tools by name ("pencil"), with Tools (Tools.Pencil) or with the enumeration
Types.Tool (Types.Tool.PENCIL).
| Tool | Tools |
Value | Description |
|---|---|---|---|
| Select | Tools.Select |
select |
Disables drawing. Objects can be selected, moved, scaled and rotated. |
| Pencil | Tools.Pencil |
pencil |
Free drawing. |
| Line | Tools.Line |
line |
Straight lines. |
| Arrow | Tools.Arrow |
arrow |
Lines with an arrow head. |
| Rectangle | Tools.Rectangle |
rectangle |
Rectangles, filled with fillColor. |
| Rectangle label | Tools.RectangleLabel |
rectangle-label |
Rectangles with a text box above them. The text box follows the rectangle when it is moved or scaled. |
| Circle | Tools.Circle |
circle |
Circles, filled with fillColor. The drag from the first to the second point is the diameter. |
| Pan | Tools.Pan |
pan |
Disables drawing. Dragging moves the whole canvas, useful after zooming (contributed by wmaillard). |
| Highlighter | Tools.Highlighter |
highlighter |
Free drawing in a semi-transparent color. lineColor must be a hex code or a color name; any other value falls back to the default color. |
| Default tool | Tools.DefaultTool |
default-tool |
Disables drawing and selection. Use it to show a drawing that is not edited. |
| Export | Description |
|---|---|
SketchField |
The component (named export). Also usable as a type, for the ref. |
Tools |
The tools by name: Select, Pencil, Line, Arrow, Rectangle, RectangleLabel, Circle, Pan, DefaultTool, Highlighter. |
Types |
Namespace of the types: Tool (enumeration), Tools, SketchProperties, SketchEvent, SketchObject, SketchValue, SketchData, DataUrlOptions, ImageOptions, BackgroundOptions, TextOptions. |
| default export | The component, which also carries SketchField and Tools (as in 0.5.x). |
opacityis accepted and not applied.onChangeis called after every mouse up and mouse out, whether or not the drawing changed (issue #80).undoStepskeeps one step more than its value:undoSteps + 1actions can be undone.- Drawing an arrow records undo steps which have no visible effect.
- With
forceValue, every update of the component (for example each re-render of the parent with new properties) loadsvalueagain.
- React:
^17.0.0 || ^18.0.0 || ^19.0.0(peer dependency). The test suite runs on React 19. - Browsers: browsers with HTML5 canvas support. No polyfills are included and Internet Explorer 11 is not supported.
- Server-side rendering has not been tested. Render the component in the browser only.
- Node.js: the package has no runtime requirement for Node.js. Developing in this repository requires Node.js 22.22.1 or later (
.nvmrchas 24) and Yarn 1.
This is a Yarn 1 workspaces monorepo.
| Path | Description |
|---|---|
packages/react-sketch |
The library, published to npm as react-sketch. The build output is dist/index.js (UMD bundle, react and fabric are external) and dist/index.d.ts (type declarations). |
packages/showcase |
Demo application (not published): a tools panel and the defaultValue, value and addImg scenarios. |
.github/workflows |
Continuous integration (ci.yml). |
Requirements: Node.js 22.22.1 or later, Yarn 1.
git clone https://github.com/tbolis/react-sketch.git
cd react-sketch
yarn install
yarn startyarn start serves the showcase at http://localhost:23000. It uses the sources of the library, so the
library does not have to be built first.
| Command | Description |
|---|---|
yarn start |
Serve the showcase on port 23000. |
yarn build |
Build the library (packages/react-sketch/dist) and the showcase (packages/showcase/build). |
yarn test |
Run the tests of the library (Jest). yarn test:watch and yarn test:coverage are also available. |
yarn typecheck |
Type-check both packages. |
yarn lint |
Lint the TypeScript sources with ESLint. yarn lint:fix fixes what it can. |
yarn format |
Format the repository with Prettier. yarn format:check only checks. |
yarn clean |
Remove the build output. |
The tests run on React 19. REACT=18 yarn test runs them on React 18, and CI does both.
1.0.0 is a rewrite in TypeScript which keeps the component API of 0.5.x: the properties, the methods,
the named exports SketchField and Tools, and the default export. It requires React 17 or later and
does not support Internet Explorer 11. A few behaviors differ deliberately and several bugs are fixed,
the changelog lists them.
See CHANGELOG.md.
Issues and pull requests are welcome at https://github.com/tbolis/react-sketch/issues. Before opening a
pull request run yarn lint, yarn typecheck, yarn test and yarn format:check. A pre-commit hook
(husky and lint-staged) runs ESLint and Prettier on the staged files. Participation in this project is
subject to the Code of Conduct.
MIT. See LICENSE.