@indiminds/charts

Dependency-free canvas charts — line, bar (vertical/horizontal), combo, pie/donut and candlestick — with configurable axes, animations and tooltips. Works with React, Next.js, Vue or plain JS.
1,124
Install
npm install @indiminds/charts
Latest Version:2.2.0
License:Proprietary
Last Updated:Oct 4, 2026
Links: npm
Maintainer: sonu-dev

@indiminds/charts

Fast, dependency-free canvas charts for trading and analytics dashboards. Line, bar (vertical or horizontal), combo, pie/donut and a full candlestick engine. Axes, animation, tooltips and theming are all configurable, and the same API works in React, Next.js, Vue, Svelte or plain JavaScript.

npm install @indiminds/charts
  • Zero runtime dependencies. No React, no D3, no charting library underneath.
  • Tree-shakeable entry points. Import only the chart you use (4–10 kB gzipped each).
  • Configurable. Hide or restyle any axis, add value labels, turn bars sideways, make a donut into a gauge.
  • Smooth by default. Intro animations, tweened live updates, respects prefers-reduced-motion.
  • Typed. Written in TypeScript, and ships .d.ts for every option and event.
  • Sharp on any screen. Handles device pixel ratio and container resizing for you.

Contents


Charts at a glance

Import Class Use it for Size (min / gzip)
@indiminds/charts/line LineChart Trends, one or two value axes 15.8 kB / 6.3 kB
@indiminds/charts/bar BarChart Grouped, horizontal, before/after (delta) bars 25.9 kB / 9.4 kB
@indiminds/charts/combo ComboChart Bars + line overlays on one category axis 20.0 kB / 7.8 kB
@indiminds/charts/pie PieChart Pie, donut, half-ring gauge 11.7 kB / 4.7 kB
@indiminds/charts CandlestickChart OHLC candles, indicators, panes, pan/zoom 26.9 kB / 9.1 kB

Each entry point is self-contained. Importing /bar never pulls in the candlestick engine.


Quick start

Every chart draws into a <canvas> inside a container you give a height. The examples use Tailwind CSS; any CSS that sets a height works the same.

<div id="chart" class="relative h-64"><canvas></canvas></div>
import { BarChart } from "@indiminds/charts/bar";

const container = document.getElementById("chart")!;
const chart = new BarChart(container.querySelector("canvas")!, container, {
  yAxis: { formatter: (v) => `${v}Cr` },
  dataLabels: true,
  tooltip: true,
});

chart.setData({
  categories: ["NSEEQ", "BSEEQ", "NSEFO", "MCX"],
  series: [{ id: "turnover", name: "Turnover", color: "#52667a", values: [2156, 1023, 648, 408] }],
});

// Later, when the element goes away:
chart.destroy();

That's the whole lifecycle. Every chart has the same shape:

  1. new Chart(canvas, container, options?)
  2. setData(data) whenever the data changes
  3. setOptions(options) whenever the look changes
  4. destroy() when you're done

Using with React / Next.js / Vue

The package has no framework dependency. Wrap it in a few lines of your own.

React / Next.js

"use client"; // Next.js App Router: canvas needs the browser

import { useEffect, useRef } from "react";
import { BarChart, type BarChartData, type BarChartOptions, type BarHoverEvent } from "@indiminds/charts/bar";

interface Props {
  data: BarChartData;
  options?: BarChartOptions;
  onHover?: (event: BarHoverEvent | null) => void;
  className?: string; // sizes the container, e.g. "h-64" or "h-full"
}

export function Bars({ data, options, onHover, className = "h-64" }: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const chartRef = useRef<BarChart | null>(null);

  // Create once, destroy on unmount.
  useEffect(() => {
    const chart = new BarChart(canvasRef.current!, containerRef.current!, options);
    chartRef.current = chart;
    return () => chart.destroy();
  }, []);

  useEffect(() => { chartRef.current?.setData(data); }, [data]);
  // "replace" so removing a prop also removes the option.
  useEffect(() => { chartRef.current?.setOptions(options ?? {}, "replace"); }, [options]);
  useEffect(() => { if (chartRef.current) chartRef.current.onHoverChange = onHover; }, [onHover]);

  return (
    <div ref={containerRef} className={`relative ${className}`}>
      <canvas ref={canvasRef} />
    </div>
  );
}
<Bars data={data} className="h-72" />            // fixed height
<div className="flex h-full flex-col">
  <Bars data={data} className="min-h-0 flex-1" /> {/* fills a panel */}
</div>

relative keeps the built-in tooltip positioned inside the chart. Use a class merge helper like cn() if your project has one. Memoize data and options (useMemo) so the chart isn't re-fed on every render. Importing the package on the server is safe. Only construct charts in the browser, which useEffect already guarantees.

Core concepts

Canvas + container

The chart watches the container with a ResizeObserver and keeps the canvas exactly that size, at your screen's pixel density. Give the container a height (h-64, h-full, flex-1 with min-h-0), because an auto-height container with only a canvas inside collapses to zero. Several charts can share a page; each owns its own canvas.

Data vs. options

  • data is what to draw: categories, series, slices. Call setData on every change. It's cheap.
  • options is how to draw it: axes, animation, labels, theme, tooltip. Pass them to the constructor and/or setOptions.

setOptions(partial) deep-merges by default, so you only pass what changes. setOptions(all, "replace") swaps the whole set. Use that to unset something, and in declarative wrappers.

Updates and animation

setData with… What you see
First data, or different categories/series The chart's intro: lines sweep in, bars grow, the pie sweeps round
Same categories and series, new values Values tween to their new positions
animation: false Instant redraw, always

Animations are skipped entirely when the user's OS asks for reduced motion. A chart created inside a hidden tab or collapsed panel holds its intro until it's visible. Call chart.replayAnimation() to play it again, e.g. when a tab is re-opened.

Hover events and tooltips

Every chart fires onHoverChange(event | null) as the cursor moves, with the hovered index, category and screen coordinates. You can:

  • use the built-in tooltip (tooltip: true), a small DOM box that follows the cursor;
  • customize it (tooltip: { formatter } or tooltip: { render });
  • or render your own from onHoverChange with any UI library. The event carries everything you need, including canvas-relative x/y.

To link an external legend, call chart.highlight(index) on legend hover and chart.highlight(null) on leave. It emphasizes that bar, slice or category without firing onHoverChange, so there's no feedback loop.

onClick(event, mouseEvent) fires when a click lands on something the chart would report on hover, with the same event. Use it to open a drill-down.

Colors

Every color option takes any CSS color, including CSS variables: "hsl(var(--primary) / 0.5)", "var(--destructive)". The chart resolves them against its own container and again whenever the theme changes, so you don't need to convert them yourself.

Custom drawing (layers)

Line, bar and combo charts take layers: { below?, above? }. Each is a function that gets the chart's geometry and draws with the plain canvas API, for marks no option covers. below runs over the grid, above over the data.

layers: {
  above: ({ ctx, frame, theme, indexToPixel, valueToPixel, activeIndex }) => {
    ctx.fillStyle = theme.text;
    ctx.fillText("Target", frame.right - 40, valueToPixel(100) - 4);
  },
}

The bar chart also passes bars: where every bar landed (index, seriesIndex, value, x, y, width, height, direction). Canvas state is saved and restored around each layer.


Recipes

Y axis only (no X axis)

new BarChart(canvas, container, {
  xAxis: { visible: false },
  yAxis: { position: "left", formatter: (v) => `${v}Cr` },
});

No axes at all

new LineChart(canvas, container, {
  xAxis: { visible: false },
  yAxis: { visible: false },
}); // a sparkline

A hidden axis gives its space back to the plot. Hide just parts with labels: false, line: false or gridLines: false.

Horizontal bars with value labels

const chart = new BarChart(canvas, container, {
  orientation: "horizontal",
  xAxis: { visible: false },
  barRadius: 3,
  dataLabels: { formatter: (v) => `${v.toLocaleString("en-IN")}Cr` },
});

chart.setData({
  categories: ["10.67L\nNSEEQ", "4.30L\nBSEEQ", "21.77K\nMCX"], // "\n" = second line
  series: [{
    id: "turnover",
    color: "#52667a",
    values: [2156.09, 1023.28, 407.58],
    colors: [null, null, "#16a34a"], // per-bar override
  }],
});

With position: "auto" (the default), each label sits inside the end of its bar when it fits and just outside when it doesn't.

Donut with a two-line center label

const pie = new PieChart(canvas, container, {
  innerRadiusRatio: 0.6,
  sliceGap: 1.5,
  centerLabel: { value: "3.18", caption: "Cr" },
  tooltip: { formatter: (v) => v.toLocaleString("en-IN") },
});
pie.setData({ slices: accounts.map((a) => ({ id: a.id, label: a.name, value: a.loss, color: a.color })) });

Half-ring gauge

new PieChart(canvas, container, {
  innerRadiusRatio: 0.7,
  startAngle: -90, // start at 9 o'clock
  sweepAngle: 180,
  centerLabel: "72%",
});

Legend hover → highlight a slice

legendRow.addEventListener("mouseenter", () => pie.highlight(i));
legendRow.addEventListener("mouseleave", () => pie.highlight(null));

Tune or disable animation

{ animation: false }                                        // instant
{ animation: { duration: 1200, easing: "easeInOutCubic" } } // slower
{ animation: { stagger: 60 } }                              // bars grow one after another
{ animation: { onUpdate: false } }                          // intro only; live updates snap

Custom tooltip content

new LineChart(canvas, container, {
  tooltip: {
    render: (e) => {
      const el = document.createElement("div");
      el.textContent = `${e.category}: ${e.points.map((p) => p.value).join(" / ")}`;
      return el; // or return a plain string, or null to hide
    },
  },
});

Strings are shown as plain text, never parsed as HTML, so data can't inject markup. Return an element for rich content.

Fixed axis range, axis titles

{
  yAxis: { min: 0, max: 100, title: "Utilisation %", line: true },
  xAxis: { title: "Time" },
}

Exact ticks and reference lines

{
  yAxis: { min: 0, max: 100, ticks: [20, 40, 60, 80, 100] },
  referenceLines: [{ value: 80, color: "var(--destructive)", dash: [4, 4], label: "Limit" }],
}

Area chart

series: [{ id: "pnl", color: "#3b82f6", values, fill: true, curve: "monotone" }]
// fill: { opacity: 0.25, baseline: 0 } fills down (or up) to a value instead of the plot's floor

Time-based x axis

Pass x (ascending numbers, e.g. epoch ms) and points are spaced by value, so an hour sampled twice takes as much width as an hour sampled sixty times.

chart.setData({ x: times, series: [{ id: "idx", color, values }] });
chart.setOptions({ xAxis: { formatter: (t) => new Date(t).toTimeString().slice(0, 5) } });

Without xAxis.ticks, the range is cut evenly with both ends labelled, as many ticks as fit. Leave some padding.right for half of the last label.

Color a line by stretch

colors gives each point its own color, which runs until the next point. Line and area both follow it.

series: [{ id: "risk", color, values, colors: values.map((v) => (v >= 70 ? red : v >= 40 ? amber : green)), fill: true }]

Point markers

series: [{
  id: "used", color, values,
  lineWidth: 0, // markers only
  points: (index, value) => ({ radius: 8, fill: value >= 100 ? red : green, text: "N" }),
}],
xAxis: { labelAngle: -60 }, // long category names
activePoint: { radius: 10 }, // drawn under the marker, so it reads as a halo

Names inside bars

series: [{ id: "turnover", color, values, labels: { text: symbols } }]

The text runs along the bar from its base and is cut with an ellipsis to what fits.

Touching pairs with an open seam

{
  barGapRatio: 0, // bars of one category touch
  maxBarThickness: 24,
}
// per series:
{ radius: [4, 0, 0, 0], strokes: values.map(() => ({ color: red, width: 2, sides: ["top", "left", "bottom"] })) }

Chart guides

LineChart

import { LineChart } from "@indiminds/charts/line";

chart.setData({
  categories: ["9:15", "9:30", "9:45"],
  series: [
    { id: "price", name: "Price", color: "#111827", values: [8300, 8312, 8309], emphasis: true },
    { id: "oi", name: "OI", color: "#3b82f6", values: [12000, 15500, 14800], axis: "right" },
  ],
});

Series with axis: "right" get their own scale on a right-hand axis (y2Axis), which only appears when such a series exists. null values leave gaps. The right axis always includes zero.

Hover event: { index, category, x, points: [{ id, value, x, y }] }, one point per series with a value at that index.

BarChart

import { BarChart } from "@indiminds/charts/bar";
  • Grouped bars: several series, one bar each per category.
  • Horizontal: orientation: "horizontal". Categories run top to bottom, yAxis is the category axis and xAxis is the value axis.
  • Per-bar styling: colors[i] and fillModes[i] ("solid" | "hatched" | "outline").
  • Before/after (delta) bars: give every series baseValues. Each bar is solid up to the smaller value, then hatched (grew) or outlined (shrank).
  • Reference line: referenceLine: { category, label, color? } draws a labelled vertical guide at a category.
  • Annotations: annotations: [{ index, seriesId, label, background, color }] hangs pill labels above specific bars.

Delta bars, reference lines and annotations are vertical-only.

Hover event: { index, category, x, y }.

ComboChart

import { ComboChart } from "@indiminds/charts/combo";

chart.setData({
  categories,
  bars: [{ id: "oi", name: "Open interest", color: "#3b82f6", values: oi }],
  lineOverlays: [
    { id: "price", name: "Price", color: "#111827", values: price, axis: "right" },
    { id: "pcr", color: "#f59e0b", values: pcr, axis: "auto", style: "dashed" },
  ],
  markers: [{ index: 5, label: "+12%", background: "#dcfce7", color: "#16a34a" }],
  bands: [{ from: 3, to: 9, color: "#16a34a", label: "Long buildup" }],
  subBars: { id: "vol", name: "Volume", color: "#a855f7", values: volume },
});
  • axis: "right" lines share a labelled right axis.
  • axis: "auto" lines each get a private, unlabelled scale. You see the shape, and read the value on hover.
  • bands tint a run of categories. markers are pills above a category's bars. subBars is a companion series on its own small scale beside the main bars.

Hover event: { index, category, x, bars: [{ id, value, color }], lines: [...] }.

PieChart

import { PieChart } from "@indiminds/charts/pie";

chart.setData({
  slices: [
    { id: "a", label: "Alpha", value: 42, color: "#3b82f6" },
    { id: "b", label: "Beta", value: 27, color: "#f59e0b" },
  ],
});

Zero and negative values draw nothing. Partial rings (sweepAngle < 360) are scaled to fill the canvas.

Hover event: { index, slice, fraction, x, y }. fraction is the slice's share of the total, 0–1, and x/y is the midpoint of its outer arc.

CandlestickChart

The full trading chart: candles, OHLC bars, Heikin Ashi, line or area price styles, indicator panes, pan/zoom and infinite history. It has its own API, separate from the options system above.

import { CandlestickChart } from "@indiminds/charts";

const chart = new CandlestickChart(canvas, container);
chart.setData(candles); // { time, open, high, low, close, volume? }[]
chart.setPriceStyle("heikin-ashi"); // "candles" | "bars" | "heikin-ashi" | "line" | "area"
chart.setIndicators(indicators); // RenderableIndicator[] from @indiminds/chart-types
chart.onCrosshairMove = (e) => {}; // { candle, index } | null
chart.onLayoutChange = (layout) => {}; // pane geometry, for positioning DOM overlays
chart.onLoadMore = (oldestTime) => {}; // fetch older candles, then setData again

setIndicators takes plain numbers. Computing them (RSI, EMA, …) is up to you. This package only turns numbers into pixels.


API reference

Methods (Line, Bar, Combo, Pie)

Member Description
new XChart(canvas, container, options?) Creates the chart. container is the element whose size the chart follows.
setData(data) Draws new data, animating per the rules in Updates and animation.
setOptions(options, mode?) "merge" (default) deep-merges; "replace" swaps the whole set.
getOptions() The current options.
highlight(index | null) Emphasizes one category, bar or slice, e.g. from a legend. Doesn't fire onHoverChange.
replayAnimation() Plays the intro again.
destroy() Stops observers, animation and listeners, and removes the tooltip. Call it on unmount.
onHoverChange (event | null) => void. null when the cursor leaves.
onClick (event, mouseEvent) => void. A click on a category, point or slice.

Shared options (ChartOptions)

Option Type Default Description
padding { top?, right?, bottom?, left? } per chart Space between the canvas edge and the plot, outside any axis.
animation boolean | AnimationOptions true See below.
theme ChartTheme from CSS variables Per-chart colors and font. See Theming.
tooltip boolean | TooltipOptions off Built-in tooltip.
crosshair boolean true The dashed guide at the hovered category.
ariaLabel string none Accessible name. Also sets role="img" on the canvas.
AxisOptions

Used by xAxis, yAxis and y2Axis.

Option Type Default Description
visible boolean true false hides the axis and frees its space.
line boolean varies The axis line itself.
labels boolean true Tick labels. Hiding them also frees their gutter.
gridLines boolean varies Gridlines at this axis' ticks. Setting true draws them even on a hidden axis.
title string none Axis name, along the outer edge.
size number per chart Px reserved for the labels.
formatter (value: number) => string per chart Numeric axes only. Also the default for data labels and tooltips.
min / max number fitted Numeric axes only. Fixes the range.
ticks number[] auto Numeric axes only. Ticks at exactly these values.
labelAngle number (deg) 0 Rotates category (and numeric x) labels, e.g. -45 or 90. The axis grows to fit.

Line, bar and combo charts also take referenceLines: { value, axis?, color?, width?, dash?, label? }[] and layers: { below?, above? }.

Category labels are drawn as given. Put "\n" in one for a second line, and the axis grows to fit.

AnimationOptions
Option Type Default Description
enabled boolean true
duration number (ms) 800 (combo 700) The intro. Value updates take ¾ of this.
delay number (ms) 0 Before the intro starts.
easing "linear" | "easeOutQuad" | "easeOutCubic" | "easeInOutCubic" | "easeOutQuart" | (t) => number "easeOutCubic"
stagger number (ms) 0 Bar only: each category starts this much after the previous one.
onUpdate boolean true Tween same-shape updates. false snaps them.
TooltipOptions<Event>
Option Type Description
enabled boolean Default true once tooltip is set.
formatter (value: number) => string Value format in the default tooltip. Defaults to the axis formatter.
render (event) => HTMLElement | string | null Replaces the default content. null hides it for that event.

The tooltip is appended to the container. If the container is position: static, it's switched to relative while the chart exists.

LineChart

Data: { categories?: string[], x?: number[], series: LineSeriesSpec[] }. Give categories, or x for a numeric axis (then categories only titles the tooltip).

LineSeriesSpec Type Description
id string Stable key.
name string? Tooltip label. Defaults to id.
color string Any CSS color.
values (number | null)[] One per category. null is a gap.
colors (string | null)[]? Per-point color, running to the next point.
axis "left" | "right" Default "left".
emphasis boolean? Slightly thicker line.
lineWidth number? Default 1.5. 0 hides the line.
curve "linear" | "monotone" Default "linear". Monotone never overshoots the data.
fill boolean | { opacity?, baseline? } Area under the line. Default opacity 0.15, down to the plot's floor.
points boolean | LinePointStyle | (index, value) => LinePointStyle | null Markers: { radius?, fill?, stroke?, strokeWidth?, text?, textColor?, font? }.

Options: ChartOptions plus xAxis, yAxis (left), y2Axis (right), referenceLines, layers and activePoint (boolean \| { radius? }, a dot on each line at the hovered point). Default formatters: left v.toFixed(2), right v.toLocaleString().

BarChart

Data: { categories, series: BarSeries[], referenceLine?, annotations? }

BarSeries Type Description
id, name?, color, values As for line series.
colors (string | null)[]? Per-bar color override.
fillModes ("solid" | "hatched" | "outline")[]? Per-bar style.
baseValues (number | null)[]? Previous values, for delta bars. Needed on every series.
strokes ({ color, width?, sides? } | null)[]? Per-bar outline inside the bar, over its fill. Leave a side out to keep that edge open.
radius number | [tl, tr, br, bl] Overrides barRadius for this series. The array sets each corner as drawn.
labels { text, color?, font? } Text inside each bar, along it from the base. color is one color or one per bar.

Options: ChartOptions plus:

Option Type Default Description
orientation "vertical" | "horizontal" "vertical"
xAxis BarAxisOptions The horizontal axis: categories when vertical, values when horizontal.
yAxis BarAxisOptions The vertical axis. position: "left" | "right" picks its side (default: right for values, left for categories).
dataLabels boolean | DataLabelOptions off Value labels on bars.
barRadius number 0 Rounds the bar's far end, in px.
barWidthRatio number 0.7 Share of the category slot the bars fill.
barGapRatio number (0–0.9) 0.15 Gap between bars of one category, as a share of each bar's slot. 0 makes them touch.
maxBarThickness number none Upper limit on bar thickness, in px. A capped group stays together.
referenceLines ReferenceLineOptions[] none Lines at fixed values on the value axis.
layers { below?, above? } none Custom drawing. The scene also has bars and orientation.
DataLabelOptions Type Default Description
position "auto" | "inside-end" | "outside-end" | "center" "auto" auto goes inside when it fits.
formatter (value, index, seriesId) => string axis formatter
color string theme text Labels outside a bar.
insideColor string #fff Labels on top of a bar.
font string bold axis font A canvas font string.
visible boolean true

ComboChart

Data: { categories, bars, lineOverlays?, markers?, bands?, subBars? }. See the guide. Series take an optional name for the tooltip.

Options: ChartOptions plus xAxis, yAxis (bars), y2Axis (right-axis lines), referenceLines and layers. The category axis draws no vertical gridlines by default.

PieChart

Data: { slices: { id, label, value, color }[] }

Options: ChartOptions plus:

Option Type Default Description
innerRadiusRatio number (0–1) 0 > 0 makes a donut.
centerLabel string | { value, caption?, color?, captionColor? } none Text in the donut hole.
startAngle number (deg) 0 Clockwise from 12 o'clock.
sweepAngle number (deg) 360 180 draws a half ring.
sliceGap number (px) 0 Transparent gap between slices.
hoverOffset number (px) 6 How far a hovered/highlighted slice pops out.

Exported types

Every entry point exports its chart's data, options and event types, plus the shared AnimationOptions, AxisOptions, ChartOptions, ChartTheme, Padding, ReferenceLineOptions, ResolvedTheme, CartesianScene, ChartLayers, PlotFrame, Easing, EasingName, TooltipOptions, TooltipModel and TooltipRow.


Theming

Charts read their colors from CSS custom properties on <html>, and redraw by themselves when its class, data-theme or style changes. Set these once and light/dark mode just works:

CSS variable Theme key Used for
--chart-grid grid Gridlines and axis lines
--chart-axis text Axis labels, titles, outside data labels
--chart-crosshair crosshair Hover guide line
--chart-pill pill Tooltip and reference-line pill background
--chart-pill-foreground pillText Tooltip and pill text
--chart-1 priceLine Candlestick line/area style

With Tailwind CSS v4, define them in your main stylesheet next to your other theme colors, and map them in @theme inline so they're also available as utilities:

/* app.css */
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));

:root {
  --chart-grid: #e5e7eb;
  --chart-axis: #6b7280;
  --chart-crosshair: #9ca3af;
  --chart-pill: #111827;
  --chart-pill-foreground: #ffffff;
}

.dark {
  --chart-grid: #313150;
  --chart-axis: #8b8b93;
  --chart-crosshair: #a1a1aa;
  --chart-pill: #313150;
}

@theme inline {
  --color-chart-grid: var(--chart-grid);
  --color-chart-axis: var(--chart-axis);
  --color-chart-pill: var(--chart-pill);
  --color-chart-pill-foreground: var(--chart-pill-foreground);
}

Toggling dark on <html> re-themes every chart. The utilities keep DOM around the chart (legends, headers, custom tooltips) in the same colors:

<div class="flex items-center gap-2 border-b border-chart-grid text-xs text-chart-axis">
  <span class="size-2 rounded-full bg-[#3b82f6]"></span> Open interest
</div>

Keep the variables on :root/.dark rather than only inside @theme, since Tailwind drops theme variables no utility uses and the charts read them at runtime.

Override per chart with theme, which wins over the CSS variables:

{ theme: { grid: "#eee", text: "#374151", fontFamily: "Inter, sans-serif", fontSize: 12 } }

Series and slice colors always come from your data.


Accessibility, performance & browser support

  • Accessibility. Canvas content is invisible to screen readers, so pass ariaLabel with a short summary, and consider a visually-hidden data table next to important charts. Motion follows prefers-reduced-motion.
  • Performance. A chart redraws only when its data, options, size, theme or the pointer changes, and animations run on requestAnimationFrame. Nothing runs while idle. Live updates of the same shape tween without re-running the intro.
  • SSR. Importing is safe on the server, since nothing touches the DOM at import time. Construct charts in the browser only.
  • Browsers. Evergreen browsers (ES2020, ResizeObserver, Canvas 2D). Rounded bar ends use CanvasRenderingContext2D.roundRect and fall back to square corners where it's missing.

Migrating from 2.0

2.x code keeps working. The fields below still work but are deprecated in favor of options:

2.0 (on data) Now (on options)
Line leftFormatter / rightFormatter yAxis.formatter / y2Axis.formatter
Bar valueFormatter yAxis.formatter (xAxis when horizontal)
Combo leftFormatter, overlay formatter yAxis.formatter, y2Axis.formatter
Combo leftAxisLabel / rightAxisLabel yAxis.title / y2Axis.title
Pie innerRadiusRatio, centerLabel same names on options
Pie valueFormatter tooltip.formatter

With default options, every chart settles on the same pixels as 2.0. Two things are new by default:

  • Bar and pie charts play an intro animation. Pass animation: false for the old instant draw.
  • Same-shape line updates tween instead of snapping.

Also fixed: value axes no longer print "-0".


License

Proprietary. Evaluation use only. See LICENSE for the full terms. Production or commercial use requires a separate agreement with Indiminds Technologies LLP (sonu@indiminds.com).

Contributing

This package lives in the Indiminds Back-Testing-UI monorepo, under packages/charting-engine. See the repo root's CLAUDE.md for the internal structure.

pnpm --filter @indiminds/charts build      # dist/ + .d.ts
pnpm --filter @indiminds/charts typecheck

Releases go through Changesets (pnpm changeset at the repo root).

Related Packages

@wick-charts/vue

Realtime, animated canvas charts for Vue: candlestick, line, bar, heatmap, pie a...

4,453 7
@tradecanvas/vue

Vue 3 component for @tradecanvas/chart — reactive props, template ref, zero boil...

2,250 27
@rekurt/openkline-vue

Vue 3 wrapper for @rekurt/openkline-core — component and useOHLCVChart composabl...

205 1
openrouter-ai

The Vercel AI SDK is **a library for building AI-powered streaming text and chat...

7,954 27,461
vue-chartjs-financial

Financial (Candlestick/Ohlc) charts with chartjs.org and npm package vue-chartjs

3,033 7

Version History

Version Released License
2.2.0 Proprietary
2.1.0 Proprietary
2.0.2 Proprietary
2.0.1 Proprietary
2.0.0 Proprietary