React Sketch Canvas
Guides

Drawing & erasing

Switch between drawing and erasing from your own toolbar.

Most drawing tools need at least two modes: adding ink and removing it. ReactSketchCanvas exposes that switch through its ref, so your toolbar can stay in React while the canvas handles pointer input.

Switching modes

Call eraseMode(true) on the canvas ref to start erasing, and eraseMode(false) to return to drawing. Keep the active mode in component state so your toolbar can render the correct button as selected and show the right width control.

Active Tool
Stroke Width5px
Eraser Width10px
Eraser Mode
App.tsx
import { Eraser, Pencil, Settings2 } from "lucide-react";import { type ChangeEvent, useRef, useState } from "react";import {	type EraserMode,	ReactSketchCanvas,	type ReactSketchCanvasRef,} from "react-sketch-canvas";export default function App() {	const canvasRef = useRef<ReactSketchCanvasRef>(null);	const [eraseMode, setEraseMode] = useState(false);	const [eraserMode, setEraserMode] = useState<EraserMode>("mask");	const [strokeWidth, setStrokeWidth] = useState(5);	const [eraserWidth, setEraserWidth] = useState(10);	const handleEraserClick = () => {		setEraseMode(true);		canvasRef.current?.eraseMode(true);	};	const handlePenClick = () => {		setEraseMode(false);		canvasRef.current?.eraseMode(false);	};	const handleStrokeWidthChange = (event: ChangeEvent<HTMLInputElement>) => {		setStrokeWidth(+event.target.value);	};	const handleEraserWidthChange = (event: ChangeEvent<HTMLInputElement>) => {		setEraserWidth(+event.target.value);	};	const handleEraserModeChange = (mode: EraserMode) => {		if (!eraseMode) {			return;		}		setEraserMode(mode);	};	return (		<div className="not-prose flex flex-col gap-4 w-full">			{/* Drafting Table Tool Panel */}			<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 p-4 rounded-lg border border-fd-border bg-fd-card shadow-sm text-fd-foreground">				{/* Tool Selectors */}				<div className="inline-flex flex-col gap-2">					<span className="text-xs font-semibold uppercase tracking-wider text-fd-muted-foreground">						Active Tool					</span>					<div className="inline-flex rounded-md p-1 bg-fd-muted border border-fd-border w-fit">						<button							type="button"							onClick={handlePenClick}							className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-md text-xs font-medium transition-all duration-200 ${								!eraseMode									? "bg-fd-primary text-fd-primary-foreground shadow-sm"									: "text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-accent/50"							}`}						>							<Pencil className="w-3.5 h-3.5" />							Pen						</button>						<button							type="button"							onClick={handleEraserClick}							className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-md text-xs font-medium transition-all duration-200 ${								eraseMode									? "bg-fd-primary text-fd-primary-foreground shadow-sm"									: "text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-accent/50"							}`}						>							<Eraser className="w-3.5 h-3.5" />							Eraser						</button>					</div>				</div>				{/* Stroke & Eraser Width Sliders */}				<div className="flex flex-1 flex-col sm:flex-row gap-4">					{/* Stroke Width Slider */}					<div						className={`flex flex-col flex-1 gap-2 transition-opacity duration-200 ${eraseMode ? "opacity-40" : "opacity-100"}`}					>						<div className="flex justify-between items-center text-xs font-semibold uppercase tracking-wider text-fd-muted-foreground">							<span htmlFor="strokeWidth">Stroke Width</span>							<span className="font-mono text-fd-foreground bg-fd-muted border border-fd-border px-1.5 py-0.5 rounded text-[10px]">								{strokeWidth}px							</span>						</div>						<input							disabled={eraseMode}							type="range"							min="1"							max="20"							step="1"							id="strokeWidth"							value={strokeWidth}							onChange={handleStrokeWidthChange}							className="w-full accent-fd-primary cursor-pointer disabled:cursor-not-allowed"						/>					</div>					{/* Eraser Width Slider */}					<div						className={`flex flex-col flex-1 gap-2 transition-opacity duration-200 ${!eraseMode ? "opacity-40" : "opacity-100"}`}					>						<div className="flex justify-between items-center text-xs font-semibold uppercase tracking-wider text-fd-muted-foreground">							<span htmlFor="eraserWidth">Eraser Width</span>							<span className="font-mono text-fd-foreground bg-fd-muted border border-fd-border px-1.5 py-0.5 rounded text-[10px]">								{eraserWidth}px							</span>						</div>						<input							disabled={!eraseMode}							type="range"							min="1"							max="20"							step="1"							id="eraserWidth"							value={eraserWidth}							onChange={handleEraserWidthChange}							className="w-full accent-fd-primary cursor-pointer disabled:cursor-not-allowed"						/>					</div>				</div>				{/* Eraser Mode Toggles */}				<div className="inline-flex flex-col gap-2">					<span className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-fd-muted-foreground">						<Settings2 className="w-3.5 h-3.5" />						Eraser Mode					</span>					<div className="inline-flex w-fit rounded-md border border-fd-border bg-fd-muted p-1">						<button							type="button"							disabled={!eraseMode}							aria-pressed={eraserMode === "mask"}							onClick={() => handleEraserModeChange("mask")}							className="inline-flex items-center rounded-md px-2 py-1.5 text-xs font-medium transition-all duration-200 hover:bg-fd-accent aria-pressed:bg-fd-primary aria-pressed:text-fd-primary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-primary disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:bg-transparent"						>							Mask						</button>						<button							type="button"							disabled={!eraseMode}							aria-pressed={eraserMode === "stroke"}							onClick={() => handleEraserModeChange("stroke")}							className="inline-flex items-center rounded-md px-2 py-1.5 text-xs font-medium transition-all duration-200 hover:bg-fd-accent aria-pressed:bg-fd-primary aria-pressed:text-fd-primary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-primary disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:bg-transparent"						>							Stroke						</button>					</div>				</div>			</div>			{/* Canvas Workspace */}			<div className="relative overflow-hidden rounded-lg border border-fd-border bg-fd-card aspect-video min-h-[240px] shadow-sm">				<ReactSketchCanvas					ref={canvasRef}					eraserMode={eraserMode}					strokeWidth={strokeWidth}					eraserWidth={eraserWidth}					strokeColor="var(--color-fd-primary)"					canvasColor="transparent"				/>			</div>		</div>	);}
DrawingToolbar.tsx
export function () {
  const  = <ReactSketchCanvasRef>(null);
  const [, ] = <"draw" | "erase">("draw");

  return (
    <>
      < ={} />
      <
        ="button"
        ={() => {
          .?.(false); 
          ("draw");
        }}
        ={ === "draw"}
      >
        Draw
      </>
      <
        ="button"
        ={() => {
          .?.(true); 
          ("erase");
        }}
        ={ === "erase"}
      >
        Erase
      </>
    </>
  );
}

Eraser mode: mask vs stroke

The eraserMode prop controls how eraser gestures are stored.

  • eraserMode="mask" (default) stores each eraser gesture as a mask path. The original strokes are not changed, so undo and redo replay the exact visual state.
  • eraserMode="stroke" deletes any drawing stroke the eraser touches. There is no eraser path in the saved state.

mask is the default for backward compatibility. Pick stroke only when you want erased strokes to be removed from the path list; for example, when the result will be re-serialized and edited later.

Choose eraser behavior
export function () {
  const [, ] = <>("mask");

  return (
    <>
      < eraserMode={} />
ReactSketchCanvasProps.eraserMode?: EraserMode | undefined

Eraser behavior used when eraseMode(true) is active or a pen eraser button is detected.

@remarksUse "mask" to preserve eraser gestures as SVG mask paths. Use "stroke" when erasing should delete whole drawing strokes touched by the eraser.@defaultValue"mask"
< ="Eraser mode" ={} ={() => (.. as )} > < ="mask">Mask</> < ="stroke">Stroke</> </> </> ); }

Stroke width

strokeWidth and eraserWidth are independent. Expose two sliders if your toolbar lets users change both. If you only show one width control, swap which prop it drives based on the current mode.

See also

On this page