Glad to announce billboard.js v4.0.0!
This major release focuses on two important areas: better performance for high-density charts and a lighter ESM experience for modern bundlers.
For more details, please check out the release notes:
What's new?
1. Canvas rendering mode
The biggest update in v4 is the new opt-in canvas rendering mode.
See it in action:
https://naver.github.io/billboard.js/demo/
billboard.js has traditionally rendered charts with SVG, which is still the default and remains the best choice for many use cases. But when rendering tens of thousands of data points, SVG can become expensive because every point, bar, tick, grid line, and label may create DOM nodes.
Use the rendering mode selector in the left sidebar to switch between SVG and Canvas.
Canvas mode reduces that cost by drawing chart primitives into a single canvas element.
import bb, {bar, canvas} from "billboard.js/canvas";
bb.generate({
render: {
mode: canvas()
},
data: {
columns: [
["data1", 30, 200, 100, 400]
],
type: bar()
}
});
The /canvas entry is a tree-shaking entry, not the runtime switch itself. The renderer switches to canvas only when render.mode is set from canvas(). Shape resolvers imported from billboard.js/canvas install canvas-compatible shape modules without importing the SVG shape renderers.
Supported canvas chart types include:
linesplinestepareaarea-line-rangearea-splinearea-spline-rangearea-steparea-step-rangebarscatterbubblecandlesticktreemap
Canvas mode is intentionally opt-in. Existing charts continue to use SVG unless render.mode is set to canvas().
The canvas entry re-exports optional API and interaction resolvers, so category(), grid(), regions(), exportApi(), flow(), selection(), subchart(), and zoom() can be imported from the same entry when needed.
import bb, {
bar,
canvas,
category,
exportApi,
flow,
grid,
regions,
selection,
subchart,
zoom
} from "billboard.js/canvas";
Arc-family charts such as pie, donut, gauge, and polar *remain SVG-only by design because they usually have a small DOM surface and do not benefit enough from a parallel canvas renderer
*. radar and funnel also remain SVG-only unless a concrete high-density use case appears.
2. Canvas chart styling with theme selectors
Canvas mode renders chart primitives into a canvas, so SVG DOM nodes such as .bb-bar-0 or .bb-target-data1 .bb-bar do not exist as styleable elements.
To make styling migration easier, v4 provides canvas.theme.selectors. It maps supported billboard.js SVG selectors to canvas drawing styles.
Reference:
https://github.com/naver/billboard.js/blob/master/CANVAS_THEME_SELECTORS.md
bb.generate({
render: {
mode: canvas()
},
canvas: {
theme: {
selectors: {
".bb-axis .tick text": {fill: "#555", font: "12px sans-serif"},
".bb-grid line": {stroke: "#ddd", "stroke-width": 1},
".bb-bar": {stroke: "#fff", "stroke-width": 1}
}
}
},
data: {
columns: [
["data1", 30, 200, 100, 400]
],
type: bar()
}
});
Canvas mode reads supported theme values from SVG CSS probes during initialization and resize, then draws pixels with the resolved values. Current probes include baseline axis/grid/shape/text styles, axis-specific tick text fonts, focus grid styles, selected/focused point styles, active x tick text, expanded bar / candlestick opacity, and HTML legend point styles.
Supported selector areas include:
- Axis:
- domain lines, tick lines, tick text, active x tick text, and axis titles
- Grid and regions:
- grid lines, grid labels, focus grid lines, regions, and region labels
- Shapes:
- bars, candlesticks, lines, areas, points, selected/focused points, and treemap borders
- Brushes and text:
- zoom brush, subchart brush, empty-data label, data labels, and chart title
canvas.theme.selectors is a compatibility mapping, not a full CSS selector engine. Unsupported selectors are ignored. CSS property names can be written in kebab-case or camelCase, for example "stroke-width" or strokeWidth. Comma-separated selector groups are supported.
Direct canvas.theme keys are also available as advanced overrides. When both selector overrides and direct keys are provided, direct keys take precedence.
3. Performance improvement for large data
The updated v4 benchmark compares rebuilt local UMD bundles from the 3.18.0 tag and the current v4 branch.
The benchmark was measured in Chromium headless with one warmup run . Values are median elapsed time in milliseconds. It uses the public benchmark demo defaults where applicable: 1 x 10,000 data matrix. Line-like charts keep the default point rendering cost, matching the demo behavior rather than a path-only micro-benchmark.
Simple averages below use the nine canvas-supported demo types so the SVG and canvas summaries are compared over the same chart set.
Across the three benchmarked operations below, initial render, chart.load(), and chart.resize(), 4.0 Canvas is 94.3% faster overall on average than 3.18.0. The 4.0 SVG path is also 47.4% faster overall on average than 3.18.0 across the same operations.
| Operation | 4.0 SVG vs 3.18.0 | 4.0 Canvas vs 3.18.0 |
|---|---|---|
| Initial render | 60.4% faster | 98.0% faster |
chart.load() |
75.7% faster | 97.5% faster |
chart.resize() |
6.1% faster | 87.3% faster |
Some initial render medians from the benchmark:
| Type | Data matrix | 3.18.0 | 4.0 SVG | 4.0 Canvas |
|---|---|---|---|---|
area |
1 x 10,000 | 1138.1 ms | 439.5 ms | 25.1 ms |
bar |
1 x 10,000 | 1152.4 ms | 458.3 ms | 44.4 ms |
bubble |
1 x 10,000 | 1330.8 ms | 511.9 ms | 20.1 ms |
line |
1 x 10,000 | 1098.3 ms | 447.4 ms | 16.7 ms |
scatter |
1 x 10,000 | 1156.6 ms | 494.8 ms | 16.4 ms |
spline |
1 x 10,000 | 1068.1 ms | 415.7 ms | 17.0 ms |
step |
1 x 10,000 | 1069.0 ms | 422.2 ms | 17.0 ms |
These numbers are benchmark snapshots, not a release guarantee. Actual results depend on chart type, browser, data shape, options, and styling.
The result shows two separate effects. The v4 SVG path is substantially faster than 3.18.0 for node-heavy axis charts in initial render and chart.load(), while resize also benefits from reusing unchanged SVG tick nodes and tick text during resize-only redraws. Canvas then gives another large gain for supported high-density axis charts because those charts avoid per-point or per-shape SVG nodes and defer clean-frame bitmap copies until an overlay is actually needed.
4. Smaller ESM bundles
v4 also improves ESM tree-shaking.
Some optional APIs that were previously included automatically are now separated into explicit ESM resolvers:
exportApiflowgridregionscategory
For ESM users, this means you import only what you use.
import bb, {
bar,
grid,
regions,
category,
exportApi,
flow
} from "billboard.js";
const chart = bb.generate({
...grid(),
...regions(),
...category(),
...exportApi(),
...flow(),
data: {
type: bar(),
columns: [
["data1", 30, 200, 100, 400]
]
}
});
If none of the newly separated optional modules are imported, a minimal bar chart ESM bundle can be about 19 KB smaller minified, or about 6.3 KB smaller gzipped, compared with the previous auto-included behavior.
UMD users are unaffected. The UMD bundle still includes these modules automatically.
5. Migration note for ESM users
This is the main breaking change in v4.
If your ESM chart uses APIs such as chart.export(), chart.flow(), chart.xgrids(), chart.ygrids(), chart.regions(), chart.category(), or chart.categories(), import and invoke the matching resolver.
- import bb, {bar} from "billboard.js";
+ import bb, {bar, grid, regions, category, exportApi, flow} from "billboard.js";
const chart = bb.generate({
+ ...grid(),
+ ...regions(),
+ ...category(),
+ ...exportApi(),
+ ...flow(),
data: {
type: bar(),
columns: [...]
}
});
When a required module is missing, billboard.js now surfaces a clearer runtime error instead of a generic chart.xxx is not a function.
MODULE_IMPORTS.md is the canonical guide for the new ESM import model. It documents both common usage styles: spreading resolvers into a single chart config, and registering resolvers once at app bootstrap so later charts can use plain v3-style config. This is useful for SPAs, code-split routes, and micro-frontend setups where import order can otherwise be surprising.
Going forward
v4 is a major step toward making billboard.js work better for both modern bundle environments and high-density visualization use cases.
SVG remains the default rendering mode and continues to provide the richest styling model. Canvas mode adds a new path for cases where rendering performance matters more than per-node DOM styling.
Please try it out and share feedback through GitHub issues or the community channels.
Happy charting!


Top comments (0)