Overview
Let users draw on any chart: lines, freehand strokes, shapes, text and measurements, with editing, undo / redo and JSON save.
"use client";
import { ChartCanvas, Pane } from "@reincharts/core";
import { LineSeries } from "@reincharts/series";
import { Grid, XAxis, YAxis } from "@reincharts/axes";
import { Crosshair } from "@reincharts/coordinates";
import { Legend } from "@reincharts/tooltip";
import {
type Drawing,
Drawings,
DrawingStyleToolbar,
DrawingToolbar,
useDrawings,
ZoomButtons,
} from "@reincharts/interactive";
import { weather, type WeatherDay } from "@/lib/data-general";
import { useDocsChartTheme } from "@/lib/use-docs-theme";
const last = weather.length - 1;
// a point on the day `back` days before the last one
const at = (back: number, value: number) => ({ time: weather[last - back].date.getTime(), price: value });
// lines, freehand, shapes, text and measurements
const tools = [
"trendLine",
"ray",
"extendedLine",
"infoLine",
"arrow",
"horizontalLine",
"horizontalRay",
"verticalLine",
"crossLine",
"brush",
"highlighter",
"rectangle",
"ellipse",
"triangle",
"path",
"text",
"callout",
"priceRange",
"dateRange",
"dateAndPriceRange",
];
const defaultDrawings: Drawing[] = [
{ id: "trend", type: "trendLine", pane: "main", points: [at(201, 6.3), at(164, 13.2)], style: {} },
{
id: "summer",
type: "rectangle",
pane: "main",
points: [at(131, 23.6), at(86, 16.4)],
style: { color: "#f59e0b", fill: "rgba(245, 158, 11, 0.1)" },
},
{
// a highlighter stroke over the first cold spell
id: "highlight",
type: "highlighter",
pane: "main",
points: Array.from({ length: 11 }, (_, i) => at(31 - i, weather[last - 31 + i].temp)),
style: { color: "rgba(255, 193, 7, 0.4)", width: 14 },
},
{
id: "note",
type: "callout",
pane: "main",
points: [at(25, 12.3), at(70, 8)],
style: { text: "First cold spell" },
},
];
export default function DrawingTools() {
const theme = useDocsChartTheme();
const drawings = useDrawings({ defaultDrawings });
return (
<div className="flex size-full">
{/* left toolbar: tool groups, magnet, keep drawing, lock, hide, remove */}
<DrawingToolbar controller={drawings} tools={tools} />
<div className="min-w-0 flex-1">
<ChartCanvas data={weather} theme={theme} barSpacing={3}>
<Pane id="main">
<Grid />
<LineSeries<WeatherDay> name="Mean temperature" y="temp" curve="monotone" />
<YAxis tickFormat={(t) => `${t.label}°`} />
<Legend />
</Pane>
<XAxis />
<Crosshair />
<Drawings controller={drawings} />
{/* floating style editor for the selected drawing */}
<DrawingStyleToolbar controller={drawings} />
<ZoomButtons />
</ChartCanvas>
</div>
</div>
);
}@reincharts/interactive turns any chart into a canvas users can mark up: a toolbar, selection and editing, magnet,
undo / redo, copy / paste, JSON save / load, crossing alerts and an API for your own tools. Drawings are rendered on
the chart canvas and stay anchored to the data while users pan and zoom; the toolbars are plain DOM.
Setup
Import the toolbar stylesheet once, create a controller with useDrawings, and render three components:
import { ChartCanvas, Pane } from "@reincharts/core";
import { LineSeries } from "@reincharts/series";
import { Drawings, DrawingStyleToolbar, DrawingToolbar, useDrawings } from "@reincharts/interactive";
import "@reincharts/interactive/styles.css";
function Chart({ data }: { data: { date: Date; temp: number }[] }) {
const drawings = useDrawings({ defaultDrawings: [] });
return (
<div style={{ display: "flex", height: 520 }}>
<DrawingToolbar controller={drawings} />
<ChartCanvas data={data}>
<Pane id="main">
<LineSeries y="temp" />
</Pane>
<Drawings controller={drawings} />
<DrawingStyleToolbar controller={drawings} />
</ChartCanvas>
</div>
);
}<DrawingToolbar>is the tool palette: tool groups with flyouts, favorites, cursor / eraser, magnet, keep drawing, lock all, hide all and remove all. Usetoolsto choose the tools andorientation="horizontal"to put it on top.<Drawings>renders and edits the drawings, each in the pane named by itspane(give your panes stable ids).<DrawingStyleToolbar>floats above the selected drawing: color, width, dash, fill, text and extend controls.- The toolbars follow a
.dark/data-theme="dark"ancestor, or taketheme, and are styled with--rc-*CSS variables.
Tools
| Group | Tools |
|---|---|
| Lines | Trend line, ray, extended line, info line, arrow, horizontal line / ray, vertical line, cross line |
| Freehand | Brush, highlighter |
| Shapes | Rectangle, ellipse, triangle, path |
| Text & callouts | Text, callout |
| Measure | Date range, value range, date & value range |
The Freehand group is for marking up a chart by hand: the brush draws smooth strokes, the highlighter thick
translucent ones. Price charts get extra tools (Fibonacci, channels, positions, price labels): see
financial drawing tools. Pass tools to show only the tools you need:
<DrawingToolbar controller={drawings} tools={["trendLine", "horizontalLine", "brush", "highlighter", "text"]} />Editing
- Draw: pick a tool and click each point (paths: click points, double-click or
Enterto finish; brush and highlighter: drag). - Select: click a drawing;
Shift-click adds to the selection. Drag the body to move, drag handles to reshape. - Magnet: "weak" snaps to nearby series values, "strong" always snaps. Hold
Ctrl/Cmdto invert it while drawing or dragging. - Constrain: hold
Shiftwhile drawing lines to snap to 45° angles. - Keep drawing: stay in the tool after finishing a drawing.
- Lock / hide: lock or hide all drawings from the toolbar; locked drawings can be selected but not moved.
- Order:
bringToFront/sendToBackon the controller.
Keyboard shortcuts
| Key | Action |
|---|---|
Esc | Cancel the drawing, leave the tool, or deselect |
Delete / Backspace | Remove the selected drawings |
Ctrl / Cmd + Z | Undo |
Ctrl / Cmd + Y, Ctrl / Cmd + Shift + Z | Redo |
Ctrl / Cmd + C / V | Copy / paste |
Ctrl / Cmd + D | Duplicate |
Enter | Finish a path |
Shift (while drawing) | Constrain angles |
Ctrl / Cmd (while drawing) | Invert the magnet |
Saving & loading
A drawing is plain JSON: anchor points in data units (time is the x value, price the y value on the pane's
scale) plus a style, so drawings survive reloads, zoom and new data.
const drawing: Drawing = {
id: "trend-1",
type: "trendLine",
pane: "main",
points: [
{ time: Date.UTC(2026, 3, 13), price: 6.3 },
{ time: Date.UTC(2026, 4, 20), price: 13.2 },
],
style: { color: "#2962ff", width: 2 },
};useDrawings(options) returns a controller with the state (drawings, selectedIds, activeTool, magnet...) and
methods: setTool, add, update, removeSelected, removeAll, lockAll, hideAll, copy, paste, undo,
redo, toJSON, load... Save with drawings.toJSON() and restore with drawings.load(saved). It works
uncontrolled (defaultDrawings) or controlled (drawings + onChange, for React state or your store); see
Saving & loading. Tool defaults (a ray's extend: "right", the highlighter's width)
are merged only into drawings created from the toolbar or with add; drawings passed to defaultDrawings or load
are drawn with exactly the style they carry. Event callbacks: onCreate, onUpdate, onRemove, onSelect,
onCross (see Alerts).
Controller, state and tool registry
useDrawingswrapscreateDrawingController(options), a framework-agnostic store. Pass the controller to child components and read its state there withuseDrawingState(controller)(for a custom toolbar, a drawing list, an undo button...).- The built-in tools are exported as
builtinTools(toolbar order), grouped aslineTools,freehandTools,fibTools,shapeTools,annotationToolsandmeasureTools, with labels intoolGroups. Each tool also has its own export (trendLine,brush,fibRetracement...). useDrawings({ builtinTools: false, tools: [...] })registers only your tools;defaultStyleapplies a style to every new drawing,DRAWING_COLORSis the style toolbar's swatch palette.
function UndoButton({ controller }: { controller: DrawingController }) {
const { canUndo } = useDrawingState(controller);
return (
<button disabled={!canUndo} onClick={() => controller.undo()}>
Undo
</button>
);
}Custom tools
A tool is a plain object built with defineTool: how many points the user places, how to draw it and how to hit-test
it. Custom tools get the toolbar button, magnet, editing, undo / redo and persistence for free; see
Custom drawing tool.