TradingView's Lightweight Charts is excellent at one thing: rendering financial data fast on a canvas. But the moment you try to build an actual trading interface, you hit the wall, it ships no drawing tools, no indicators, no replay, no multi-pane sync. Those are left to you.
So every project, I found myself doing the same archaeology: drawing tools in one community repo, an indicator implementation in another, a replay system I'd write from scratch, and three different API styles to glue together. I got tired of it and packaged the whole layer into one library: CandleKit (MIT).
The idea
One tree-shakeable API over Lightweight Charts that includes the pieces you keep rebuilding:
- Drawing tools — trendline, ray, Fibonacci, rectangle, circle, horizontal/vertical lines, with select / drag / reshape / lock / persist. Original engine on LWC canvas primitives, no third-party drawing runtime.
- Indicators — SMA, EMA, WMA, VWAP, Bollinger, RSI, MACD, ATR, Stochastic built in, plus an extensible registry so you can register your own.
- Deterministic replay — play/pause, step ±1, speed control, seek/jump, per-bar hooks.
-
Measurement ruler, multi-pane sync, and a plugin system where drawing/indicators/measurement are all swappable
ChartPlugins.
The core is framework-agnostic — no React, no DOM framework. There's an optional /react entry with components and hooks.
A chart in React in ~10 lines
import { ChartView } from "@getcandlekit/charts/react";
import "@getcandlekit/charts/styles.css";
export function App({ bars }) {
return (
<div style={{ height: 480 }}>
<ChartView data={bars} seriesType="candlestick" theme="dark" />
</div>
);
}
Install is just the package plus Lightweight Charts as a peer dependency:
npm install @getcandlekit/charts lightweight-charts
Adding drawing tools
Drop a toolbar in and you get the full drawing engine with persistence:
import { ChartView, DrawingToolbar } from "@getcandlekit/charts/react";
<ChartView data={bars} drawing={{ storageKey: "drawings:AAPL" }}>
<DrawingToolbar />
</ChartView>;
Adding indicators
import { ChartView, IndicatorPicker, IndicatorController, createBuiltinRegistry } from "@getcandlekit/charts/react";
const indicators = new IndicatorController(createBuiltinRegistry());
indicators.add("RSI", { length: 14 });
indicators.add("EMA", { length: 21 });
<ChartView data={bars} indicators={indicators}>
<IndicatorPicker />
</ChartView>;
And registering a custom indicator is one call — this is the real extension point:
import { IndicatorRegistry } from "@getcandlekit/charts";
const registry = new IndicatorRegistry().register({
name: "PriceMid",
title: "HL/2",
shortTitle: "MID",
category: "overlay",
defaultInputs: {},
inputConfig: [],
plotConfig: [{ id: "mid", color: "#f59e0b" }],
hlineConfig: [],
calculate: (bars) => ({
plots: { mid: bars.map((b) => ({ time: b.time, value: (b.high + b.low) / 2 })) },
}),
});
Deterministic replay
import { createReplayController } from "@getcandlekit/charts";
const replay = createReplayController();
replay.onBar((e) => chart.updateBar(e.bar));
await replay.load({
id: "demo",
series: [{ symbol: "AAPL", interval: "1m" }],
start: Date.parse("2024-01-02T14:30:00Z"),
end: Date.parse("2024-01-03T21:00:00Z"),
source: myReplayDataSource,
});
replay.setSpeed(8);
replay.play();
Same bars, same result every run, which is what you want for backtesting UIs or teaching a setup step by step.
Try it
There's a live workspace demo with drawing, indicators, measurement and replay in one chart: https://rohanbeingsocial.github.io/candlekit-charts/workspace/
Repo (MIT): https://github.com/rohanbeingsocial/candlekit-charts
A note on honesty: CandleKit is AI-assisted ("vibe-coded"), tested and documented, but early-stage, pin a version and read the changelog before upgrading. If you build trading UIs on Lightweight Charts, I'd genuinely love feedback on the indicator and plugin APIs. Stars help others find it, but issues and PRs help more.

Top comments (0)