F FacetViz
▶ Playground
Capability guide

Learn the tool by building with it.

Each feature answers three questions: what it solves, which option unlocks it, and what the smallest useful configuration looks like.

  1. 01
    Choose a data shapeNumbers, pairs, or objects
  2. 02
    Compose the viewSeries, axes, and layouts
  3. 03
    Add meaningLabels, context, and interaction
  4. 04
    Ship confidentlyResponsive, accessible, validated
01
Start with data

Use the shape that matches the question.

Only series is required. Add explicit coordinates or point objects when the chart needs more meaning.

Foundationseries[].data

Three input shapes, one consistent API

Use number arrays for categories, coordinate pairs for continuous axes, and objects for names, colors, ranges, metadata, or chart-specific fields.

12Category or index + value
[x, y]Explicit coordinates
{ x, y, … }Rich point metadata
See every point shape →
Live resultTry the config below
ConfigurationChartOptions

Describe the result, not the drawing steps

The options tree keeps data, layout, labels, and behavior together. Global defaults go in plotOptions.series; type-specific defaults go in plotOptions.column, plotOptions.line, and so on.

new FacetViz('#chart', {
  chart: { type: 'column' },
  xAxis: { categories: ['A', 'B', 'C'] },
  plotOptions: { column: { borderRadius: 5 } },
  series: [{ name: 'Orders', data: [12, 18, 15] }],
});
Chart familieschart.type

Choose from 35+ focused renderers

Cover comparison, change, distribution, relationship, flow, hierarchy, schedules, finance, and compact KPI views. Set one default type on the chart or override it per series.

columnlinescatterboxplotheatmapsankeyganttgauge
Compare every chart type →
02
Compose a view

Combine marks, scales, and dimensions.

Build one chart from several series, or repeat a consistent chart across a grid of categories.

Compositionseries[].type

Mixed series and secondary axes

A column can show volume while a spline shows a rate. Bind the differently scaled series to yAxis: 1 and place that axis on the right.

Use it whenTwo measures share categories but not units.
Open the full combo example →
Live resultVolume + rate
Series layoutstacking

Group, stack, normalize, or overlap

Series group side-by-side by default. Use 'normal' for totals, 'percent' for shares, a stack id for independent groups, or grouping: false for target-vs-actual overlays.

plotOptions: {
  column: { stacking: 'percent' },
},
series: [
  { name: 'Organic', data: [40, 55, 62] },
  { name: 'Paid', data: [60, 45, 38] },
]
See stacking live →
ScalesxAxis / yAxis

Categories, numbers, dates, and logs

Axes infer sensible scales, while type makes intent explicit. Add a crosshair, reverse a scale, rotate crowded labels, or place reference lines and bands at domain values.

xAxis: {
  type: 'datetime',
  crosshair: true,
},
yAxis: {
  type: 'log', min: 1,
  labels: { format: '{value:,.0f}' },
}
Multidimensional layouttrellis

Small multiples without manual chart grids

Point fields become row and column headers. FacetViz splits the data, keeps axes aligned, and renders every series inside each cell.

Use it whenYou need to compare the same measure across two dimensions without mixing every group into one plot.
Explore trellis combinations →
Live resultRegion × channel
Projectionchart.polar

Project familiar series into a polar frame

Line, area, scatter, column, and dumbbell series can share angular and radial axes. Add a center hole, sector grid, or invert the projection for concentric arc bars.

Use it whenDirection, seasonality, or a circular cycle is part of the data’s meaning.
Live resultSame column API, new projection
03
Explain the data

Add context where readers need it.

Use one formatting language across axes, tooltips, and labels, then anchor commentary to real data values.

Formatting{path:spec}

Consistent labels from one token engine

Format currency, percentages, SI abbreviations, dates, and custom point fields without rewriting callbacks. The same syntax works on axes, data labels, and tooltips.

{y:$,.1s}$1.3M{percentage:.0f}%24%{x:%b %d}Aug 05
Formatting token reference →
Live resultCurrency + data labels
Contextannotations / plotBands

Keep explanations attached to the data

Annotations use axis values, so they survive resizing and domain changes. Plot lines mark a target; plot bands identify a meaningful range; chart text adds watermarks or status notes.

Use it whenA reader needs to know what changed, where a target sits, or which range is healthy.
Live resultTarget band + callout
Visual systemtheme

Start from a theme, then override tokens

Use 'light', 'dark', 'high-contrast', or 'pastel'. A custom theme can extend a built-in and define palette, typography, axes, labels, legend, and tooltip colors.

theme: {
  base: 'dark',
  backgroundColor: '#0b1021',
  colors: ['#00f5d4', '#f15bb5', '#fee440'],
  axis: { gridLineColor: '#1c2540' },
}
Build a theme visually →
Reading aidslegend / tooltip

Reveal detail without crowding the chart

Place the legend on any side and click its items to toggle series or slices. Tooltips can be shared across a category or customized per series with a format string or formatter.

legend: { layout: 'vertical', align: 'right' },
tooltip: {
  shared: true,
  format: '<b>{series.name}</b>: {y:$,.0f}',
}
04
Add interaction

Let people inspect and navigate.

Start with built-in hover and keyboard behavior, then add callbacks or progressive drill-down only where they help.

Explorationzoom / drilldown

Move from overview to detail

Enable drag-to-zoom on numeric or datetime axes. For categorical detail, connect a point’s drilldown id to a child series; FacetViz adds the return path.

chart: { type: 'line', zoom: 'x' },
xAxis: { type: 'datetime', crosshair: true },

// Or on a point:
{ name: 'Apples', y: 12, drilldown: 'apples' }
Try drill-down →
Application hooksseriesEvents / chart.on()

Connect chart gestures to your product

Handle point clicks, hover, legend toggles, render cycles, and drill-down. Event payloads include series, point indexes, coordinates, the original point object, and DOM event.

const chart = new FacetViz('#chart', options);

const off = chart.on('point:click', (event) => {
  openDetails(event.point.customerId);
});

// Later: off();
Inclusive interactionaccessibility

Keyboard and screen-reader support by default

One Tab stop enters the chart. Arrow keys move between points, Home and End jump, and Enter or Space activates. Each mark receives a generated description that you can replace with domain language.

Try itTab into the chart on the right, then use the arrow keys. Focus follows the data and opens the matching tooltip.
Accessibility options →
Keyboard readyTab, then use ← →
05
Ship to production

Design for real containers and real data.

Responsive rules adapt information density, while canvas rendering keeps high-volume plots useful.

Layoutresponsive

Change the config when space changes

The SVG never overflows its parent and charts reflow when their container resizes. Add size conditions to hide secondary content, rotate labels, or simplify a small view.

responsive: [{
  condition: { maxWidth: 480 },
  options: {
    legend: { enabled: false },
    xAxis: { labels: { autoRotation: [0, -45] } },
  },
}]
Performancechart.boost

Switch dense series to canvas automatically

Point and line series cross a configurable threshold into canvas rendering, with line decimation for dense signals. SVG export still embeds the canvas image.

chart: {
  type: 'scatter',
  boost: { enabled: true, threshold: 1500 },
},
series: [{ data: oneHundredThousandPoints }]
Open the 30k-point demo →
Correctnessvalidation

Catch bad configuration before it renders

Validate unknown input without side effects, or enable automatic checks on construction and data updates. Issues include a stable code, severity, option path, message, and suggested fix.

import { validateChartOptions } from 'facetviz';

const result = validateChartOptions(candidate);
if (!result.valid) console.table(result.errors);

new FacetViz('#chart', { ...options, validation: { mode: 'error' } });
Bundle controlfacetviz/core

Ship only the renderer families you use

The full entrypoint registers every chart type. For a smaller application bundle, import the core and register selected series families through side-effect imports.

import { FacetViz } from 'facetviz/core';
import 'facetviz/series/line';
import 'facetviz/series/pie';

new FacetViz('#chart', options);
06
Update & extend

Treat a chart as a living application component.

Update data without replacing the chart, export on demand, or register a renderer for a domain-specific mark.

Runtime APIsetData / appendData

Update a series without rebuilding your UI

Replace, append, or batch several changes into one render. maxPoints makes a rolling window simple, and nested batches roll back safely if an update fails.

chart.appendData(0, incomingPoints, { maxPoints: 120 });

chart.batchUpdate(() => {
  chart.setData(0, revenue);
  chart.update({ title: { text: 'Live revenue' } });
});
OutputgetSVG / downloadPNG

Export the chart your user already sees

Serialize a standalone SVG, trigger SVG or PNG downloads, or receive a PNG Blob for your own upload and sharing flow.

const svg = chart.getSVG();
chart.downloadSVG('quarterly-revenue.svg');
chart.downloadPNG('quarterly-revenue.png', 2);

const blob = await chart.toPNGBlob(2);
ExtensibilityregisterSeriesType

Own a renderer when your domain needs one

Extend BaseSeries, declare capabilities, and register a stable type name. Custom types participate in the same scales, tooltips, events, themes, and validation flow as built-ins.

import { BaseSeries, registerSeriesType } from 'facetviz';

class ThresholdSeries extends BaseSeries {
  render(context) { /* draw with context.renderer */ }
}

registerSeriesType('threshold', ThresholdSeries);
Ready to build?

Move from feature to working chart.

Start from a live example, open it in the playground, then keep the API reference nearby for exact types and defaults.