Saving & loading
Persist drawings as JSON, in controlled or uncontrolled mode.
"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 { type Drawing, Drawings, DrawingStyleToolbar, DrawingToolbar, useDrawings } from "@reincharts/interactive";
import { useState } from "react";
import { ToolbarButton, WithToolbar } from "@/components/preview/controls";
import { weather, type WeatherDay } from "@/lib/data-general";
import { useDocsChartTheme } from "@/lib/use-docs-theme";
const KEY = "reincharts-docs-drawings";
const read = (): Drawing[] => {
try {
return JSON.parse(localStorage.getItem(KEY) ?? "{}").drawings ?? [];
} catch {
return [];
}
};
export default function SaveAndLoad() {
const theme = useDocsChartTheme();
// controlled mode: the drawings live in React state (or your store / server)
const [saved, setSaved] = useState<Drawing[]>(read);
const [current, setCurrent] = useState<readonly Drawing[]>(saved);
const drawings = useDrawings({ drawings: current, onChange: setCurrent });
return (
<WithToolbar
toolbar={
<>
<ToolbarButton
onClick={() => {
localStorage.setItem(KEY, JSON.stringify(drawings.toJSON()));
setSaved([...current]);
}}
>
Save
</ToolbarButton>
<ToolbarButton onClick={() => drawings.load(saved)}>Load saved</ToolbarButton>
<ToolbarButton onClick={() => drawings.undo()}>Undo</ToolbarButton>
<ToolbarButton onClick={() => drawings.redo()}>Redo</ToolbarButton>
<ToolbarButton onClick={() => drawings.removeAll()}>Clear</ToolbarButton>
<span className="text-xs text-fd-muted-foreground">
{current.length} drawings · {saved.length} saved
</span>
</>
}
>
<div className="flex size-full">
<DrawingToolbar
controller={drawings}
tools={[
"trendLine",
"horizontalLine",
"brush",
"highlighter",
"rectangle",
"text",
"callout",
"dateRange",
]}
/>
<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}°`} />
</Pane>
<XAxis />
<Crosshair />
<Drawings controller={drawings} />
<DrawingStyleToolbar controller={drawings} />
</ChartCanvas>
</div>
</div>
</WithToolbar>
);
}Drawings are plain JSON (anchor points in data units plus a style), so they can be stored anywhere: local storage, your database, a URL.
Uncontrolled
Pass the saved drawings as defaultDrawings and save on every change:
const drawings = useDrawings({
defaultDrawings: JSON.parse(localStorage.getItem("drawings") ?? "[]"),
onChange: (all) => localStorage.setItem("drawings", JSON.stringify(all)),
});Controlled
Keep the drawings in your own state (React state, Redux, Zustand, a server cache) with drawings + onChange. Undo /
redo, copy / paste and every edit go through onChange:
const [items, setItems] = useState<readonly Drawing[]>(initial);
const drawings = useDrawings({ drawings: items, onChange: setItems });toJSON and load
drawings.toJSON() returns { version: 1, drawings }, and drawings.load(json) accepts that object, a plain array or
a JSON string. Loading is recorded in the undo history.
await fetch(`/api/layouts/${id}`, { method: "PUT", body: JSON.stringify(drawings.toJSON()) });
drawings.load(await (await fetch(`/api/layouts/${id}`)).json());Drawings reference panes by id, so give your <Pane>s stable ids. Indicators and panes are plain props of your
components; save their configuration next to the drawings (e.g. { drawings, indicators: [{ id: "rsi", inputs }] }).
The whole chart layout
With <TradingChart>, ref.save() returns a TradingChartState with everything at once:
symbol, interval, chart type, scale mode, indicators, drawings, compare symbols, pane sizes, visible range, theme and
multi-chart layout. ref.load(state) restores it. See State, save & load for the JSON
format and controlled mode.