trellis

Configuration Structure

trellis: {
by: string | ((series: any, index: number) => string | ...,
row: string | ((series: any, index: number) => string | ...,
column: string | ((series: any, index: number) => string | ...,
emptyPanels: 'placeholder' | 'skip' | ...,
data: Record<string, any>[],
x: string,
y: string,
seriesBy: string,
columns: number | 'auto',
minPanelWidth: number,
gap: number,
aspectRatio: number,
panelHeight: number,
order: 'first-seen' | 'asc' | ...,
limit: number,
virtualize: 'auto' | boolean,
scales: { ... },
x: 'shared' | 'independent',
y: 'shared' | 'independent' | ...,
color: 'shared',
size: 'shared',
header: { ... },
show: boolean,
formatter: Function,
style: { ... },
fontSize: string,
fontWeight: string | number,
color: string,
axes: { ... },
labels: 'edges' | 'all' | 'none',
legend: 'shared' | 'none',
toolbar: 'shared' | 'none',
tooltip: 'panel' | 'sync' | 'grid',
zoom: 'sync' | 'none',
promote: boolean,
radiusByTotal: boolean,
targetTicks: number,
panel: Function,
}

Split one series array into a grid of panels, each a real chart of the host's chart.type. Setting by (or row / column) makes the chart a trellis host, and the host owns everything shared between panels: the scale domains, the pixel-aligned plot rectangles, the colour-by-series-name map, the headers, one legend, one toolbar and the responsive column count.

Requires the Trellis feature, which is not in the default bundle: import 'apexcharts/features/trellis', or add dist/features/trellis.js after apexcharts.js on a script-tag page. See the Trellis guide for a walkthrough.

by

Default: undefined

Facet accessor: the name of a key on each series object, or a function receiving (series, index) and returning the panel key.

series: [
  { name: 'Revenue', region: 'North', data: [/* … */] },
  { name: 'Revenue', region: 'South', data: [/* … */] },
],
trellis: {
  by: 'region',
}

Series without the key repeat in every panel, which is how a target line or a benchmark reaches all of them at once. facet is the typed field on a series object, but any key name works from plain JavaScript.

Mutually exclusive with row / column.

row

Default: undefined

Row facet accessor for a 2-D grid. With row and column set, the grid is every (row, column) combination in row-major order with a fixed column count: column labels draw once across the top, row labels once down the left, and panels shrink rather than re-column on resize.

A series carrying only the row key repeats across that row, which makes it a row-scoped reference series.

column

Default: undefined

Column facet accessor for a 2-D grid. See row. A series carrying only the column key repeats down that column.

emptyPanels

Default: 'placeholder'

What to do with (row, column) combinations that carry no data.

Available Options:

  • 'placeholder' (default) mounts a real empty panel at the shared geometry with a quiet noData.text label
  • 'skip' keeps the slot with a tinted blank
  • 'hide' keeps the slot with nothing in it

All three keep the slot, so the grid stays rectangular and the row and column labels keep lining up.

data

Default: undefined

Tidy-row input, an alternative to series: a row table pivoted by the by, x, y and seriesBy column names, all of which must be strings in this form.

trellis: {
  data: [
    { month: '2025-01', region: 'North', revenue: 58 },
    { month: '2025-01', region: 'South', revenue: 42 },
  ],
  by: 'region',
  x: 'month',
  y: 'revenue',
}

Rows win over series when both are given. Aggregation is out of scope: duplicate (panel, series, x) rows keep the last one and warn, so sum or average the rows yourself first.

x

Default: undefined

Name of the x-value column. Tidy-row form only.

y

Default: undefined

Name of the y-value column. Tidy-row form only.

seriesBy

Default: undefined

Name of the column whose values become series names. Tidy-row form only; absent means one series per panel, named after the y column.

columns

Default: 'auto'

Column count. 'auto' fits as many minPanelWidth columns as the container holds and re-columns on resize; a number fixes the count.

minPanelWidth

Default: 220

Narrowest a panel may get, in pixels. Drives columns: 'auto' and the responsive collapse.

gap

Default: 12

Gap between cells, in pixels.

aspectRatio

Default: 1.6

Panel width divided by panel height, used when no explicit height governs.

panelHeight

Default: undefined

Explicit panel height in pixels. Takes precedence over aspectRatio and chart.height.

order

Default: 'first-seen'

Panel order.

Available Options:

  • 'first-seen' (default) follows the order the keys appear in the data
  • 'asc' / 'desc' sort the keys
  • an array of keys gives an explicit order
  • a comparator receiving (a, b) sorts them

limit

Default: undefined

Render only the first N panels in order, and warn about the rest. Useful for a top-N grid without pre-slicing the data.

virtualize

Default: 'auto'

Mount only the panels near the viewport.

Available Options:

  • 'auto' (default) virtualizes once the grid exceeds 64 panels
  • true always virtualizes
  • false always renders eagerly

An unmounted cell keeps its header and a fixed-height skeleton, so page height and scroll position never shift. A panel scrolled out of view is destroyed with its view state stashed, and a remount restores its zoom window. getPanel(key) returns null for an unmounted panel.

scales

How each visual channel resolves across panels. A shared domain is what makes panels comparable; an independent one is what makes a small panel readable.

x

trellis.scales.x
Default: 'shared'

Available Options:

  • 'shared' (default)
  • 'independent'

y

trellis.scales.y
Default: 'shared'

Available Options:

  • 'shared' (default) gives every panel the union domain, so a taller shape really is a bigger number
  • 'independent' gives every panel its own domain
  • 'independent-row' shares one domain per row (2-D grids)
  • 'independent-column' shares one domain per column (2-D grids)

Panels stay pixel-aligned in every mode: a gutter pass measures each panel's axis-label width and pushes the widest as a shared floor, so the plot rectangles agree to the pixel. 'independent' and 'independent-column' show y labels on every panel, since the ticks differ; 'independent-row' keeps them on the first column, since the ticks along a row are identical.

color

trellis.scales.color
Default: 'shared'

Colour is keyed by series name across the whole grid, so one series is one colour in every panel.

Available Options:

  • 'shared' (default)

size

trellis.scales.size
Default: 'shared'

Bubble radius resolution. Shared means one z extent for the grid, so bubble areas compare across panels.

Available Options:

  • 'shared' (default)

show

trellis.header.show
Default: true

Available Options:

  • true (default)
  • false

formatter

trellis.header.formatter
Default: undefined

Rewrite a header label. Receives (key, { dimension, index, count }) and returns the string to display. dimension names which accessor produced the key in a 2-D grid.

header: {
  formatter: (key, { index, count }) => `${key} (${index + 1}/${count})`,
}

fontSize

trellis.header.style.fontSize
Default: undefined

Font size of the header label, as a CSS length.

fontWeight

trellis.header.style.fontWeight
Default: undefined

Font weight of the header label.

color

trellis.header.style.color
Default: undefined

Colour of the header label.

axes

Axis-label policy across the grid.

labels

trellis.axes.labels
Default: 'edges'

Available Options:

  • 'edges' (default) shows y labels on the first column and x labels on each column's bottom panel
  • 'all' labels every panel
  • 'none' labels none

Label space is reserved on every panel whatever the setting, so panels stay aligned and switching modes never re-flows the grid.

legend

Default: 'shared'

One legend for the grid, whose entries toggle a series name in every panel at once. Per-panel legends are hidden.

Available Options:

  • 'shared' (default)
  • 'none'

toolbar

Default: 'shared'

One zoom, pan and reset toolbar for the grid.

Available Options:

  • 'shared' (default)
  • 'none'

tooltip

Default: 'panel'

Where the tooltip card appears on hover. The crosshair sweeps every panel in all three modes.

Available Options:

  • 'panel' (default) shows a card only in the hovered panel
  • 'sync' shows each panel its own card at the hovered x
  • 'grid' shows one card near the cursor with a row per panel at the hovered x

'grid' composes its rows from the panels' own tooltips, so every formatter is honoured. Virtualized panels that are not mounted contribute no row. Pair it with tooltip.compact when a normal card would cover a small panel.

zoom

Default: 'sync'

Whether a zoom or pan in one panel moves the others.

Available Options:

  • 'sync' (default)
  • 'none'

promote

Default: true

Let a click on a cell header expand that panel to the grid's full width, with an "All panels" breadcrumb back. Also available as promotePanel(key) and restorePanels().

Available Options:

  • true (default)
  • false

radiusByTotal

Default: false

Pie, donut and polarArea only: scale each panel's radius so its area is proportional to that panel's total.

Equal-size pies cannot encode magnitude, so a grid of them says nothing about which slice of the business is bigger. This is what makes a pie trellis honest.

Available Options:

  • false (default)
  • true

targetTicks

Default: 3

Tick-interval target for the shared nice y scale. Panels are small, so the default of 3 intervals (about four labels) keeps the axis from outweighing the data.

panel

Default: undefined

Per-panel option override, merged last. Receives (key, { index, seriesNames }) and returns a partial options object.

trellis: {
  by: 'store',
  panel: (key, { index }) => ({
    colors: [scoreOf(key) >= 85 ? '#15803D' : '#DC2626'],
  }),
}

Use it for what genuinely differs per panel. Anything shared belongs in the host options, so the trellis can keep the panels aligned.