Skip to content

Packages

Sparklines

The 4.8 KB build for tables and cards: line, area, column and win-loss.

The 5.4 KB Build

@ortex-charts/lite is a separate package with one job: a tiny canvas chart for a table cell or a card. It is 4.8 KB gzipped, depends only on @ortex-charts/math, and has no axes, no crosshair, no legend and no interaction. A table with 300 sparklines schedules one animation frame, not 300.

npm install @ortex-charts/lite
import { createSparkline } from "@ortex-charts/lite";

const spark = createSparkline(cell, { kind: "area", colorByTrend: true, width: 120, height: 28 });
spark.setData(closes);
Sparkline table@ortex-charts/lite
Loading sparkline table
Twelve rows of line, area, column and win-loss sparklines updating together on one render frame.

Open this example with its source

Four Kinds

Kind Draws Used for
line A polyline, optionally curved, with an endpoint dot. Price and value history.
area The same line with a fill beneath it. The same, with more weight in a dense table.
column One column per value, colored by sign around baseline. Returns, flows, changes.
winloss Equal-height marks up or down by sign. Win and loss streaks, hit rates.
createSparkline(el, { kind: "line" });
createSparkline(el, { kind: "column", baseline: 0 });
createSparkline(el, { kind: "winloss", baseline: 0 });

Data

spark.setData([12.4, 12.9, 12.1, 13.4]);                  // numbers
spark.setData([{ value: 12.4 }, { value: 12.9 }]);        // rows
spark.setData([[t1, 12.4], [t2, 12.9]]);                  // [time, value] pairs

Time is accepted but not used for spacing: points are evenly spaced. A sparkline is a shape, not a chart, and a gap in the dates does not become a gap in the line.

spark.push(13.1);          // append one value
spark.push(13.1, 60);      // append, keeping at most 60 points

push is the live path. It is cheap enough to call on every tick of a streaming table.

Options

Option Default What it does
kind "line" One of the four above.
width, height container size, else 100 by 28 Canvas size in CSS pixels.
color "#4C8DFF" Line or column color.
positiveColor "#22C08A" Used by colorByTrend and by sign coloring.
negativeColor "#EF4E5A" The same, downward.
colorByTrend false Color the whole sparkline by whether the last value is above the first.
lineWidth 1.5 Stroke width.
curve "linear" linear, step, monotone or smooth.
fillOpacity 0.18 Area fill opacity.
endpoint true Dot at the last point.
endpointRadius 2 Its radius.
baseline null Draw a faint line at this value and color columns by sign around it.
baselineColor a faint gray Color of that line.
min, max data extent Fixed value range, for comparability across rows.
padding 2 Inset in CSS pixels.
extremes false Highlight the minimum and maximum points.
background "transparent" Canvas background.

SPARKLINE_DEFAULTS is exported if you want to derive from the defaults.

The two options that matter most in a table are colorByTrend and min/max. colorByTrend is what makes a column of sparklines readable at a glance without a legend. Fixing min and max makes rows comparable to each other; leaving them unset makes each row use its full height, which reads better per row and worse down the column. Choose deliberately.

The Handle

const spark = createSparkline(el, options);

spark.canvas;                       // the <canvas> element, appended to the container
spark.setData(values);
spark.push(value, maxPoints?);
spark.applyOptions({ kind: "column", color: "#F5A524" });
spark.resize(160, 32);
spark.invalidate();                 // schedule a redraw
spark.flush();                      // draw now, synchronously
spark.remove();                     // detach and stop

Every mutator returns this, so calls chain. flush() is the one to call before taking a screenshot or asserting in a test, because drawing is otherwise deferred to the next frame.

The Shared Render Loop

Every sparkline on the page schedules its redraw on one RenderScheduler, exported as renderScheduler. A table that updates 300 cells in a loop produces one animation frame with 300 draws, not 300 frames. This is the reason the package exists separately from @ortex-charts/core, and it is why the cost of a sparkline table is proportional to the data rather than to the number of cells.

You do not have to do anything to get this. It applies automatically.

In a Table

import { createSparkline, type Sparkline } from "@ortex-charts/lite";

const sparks = new Map<string, Sparkline>();

for (const row of rows) {
  const cell = document.querySelector<HTMLElement>(`#spark-${row.id}`)!;
  const spark = createSparkline(cell, {
    kind: "area",
    colorByTrend: true,
    width: 120,
    height: 26,
    endpoint: true,
  });
  spark.setData(row.closes);
  sparks.set(row.id, spark);
}

// Live: one push per row, one frame for all of them.
feed.subscribe((tick) => sparks.get(tick.id)?.push(tick.price, 120));

// Teardown, when the table unmounts.
for (const s of sparks.values()) s.remove();

Beside a Line of Text

A sparkline sized to the type next to it is the densest useful form, and it needs no chrome at all.

createSparkline(span, {
  kind: "line",
  width: 56,
  height: 14,
  lineWidth: 1,
  endpoint: false,
  padding: 1,
  colorByTrend: true,
});

Keep the container display: inline-block with line-height: 0 so the canvas sits on the text baseline instead of adding a descender's worth of space.

Sparklines beside the text@ortex-charts/lite
Loading sparklines beside the text
The 4.8 KB build: a sparkline sized to the line of type next to it, with no chart chrome at all.

Open this example with its source

In React

import { Sparkline } from "@ortex-charts/react";

<Sparkline data={closes} width={120} height={28} options={{ kind: "area", colorByTrend: true }} />

The component creates the sparkline on mount, applies data and option changes in place, and removes it on unmount. The instance is available through ref when you want push. See React.

When to Use the Full Chart Instead

@ortex-charts/lite is deliberately small, and the things it does not do are the reason it is small.

  • No axes, no ticks, no labels. If you need a scale, use @ortex-charts/financial or @ortex-charts/viz.
  • No crosshair, no tooltip, no hit testing. Hovering does nothing. A tooltip on a sparkline is a title attribute or your own overlay on the container.
  • No time axis. Points are evenly spaced regardless of their timestamps.
  • No theme object. Colors are options on the sparkline, not tokens, because pulling in the theme would pull in the engine.
  • No multiple series. One array of numbers per sparkline.

The crossover point in practice is a tooltip. As soon as a small chart needs to tell the user what a point is worth, it wants createFinancialChart with most of the chrome switched off, which is about 56 KB gzipped instead of 4.8 KB.