Skip to content

Clustered bar ​

Side-by-side bars that compare series within each category.

Identity ​

chartTypeclusteredBar
Option bagbar
seriesData layoutseriesRows – row 0 = category header; later rows = series
Min grid2 rows × 2 columns (header included)
OrientationYes – vertical (default) or horizontal
SampleClustered bar · Horizontal bar

Minimal chartData ​

js
{
  chartType: 'clusteredBar',
  isDataTransposed: true,
  seriesData: [
    ['', 'Q1', 'Q2', 'Q3', 'Q4'],
    ['Revenue', 100, 112, 125, 140],
    ['Costs', 60, 66, 70, 78],
  ],
  title: { visible: true, text: 'Revenue vs Costs' },
  subtitle: { visible: false, text: '' },
}

seriesData ​

With isDataTransposed: true (usual):

js
[
  ['', 'Q1', 'Q2', 'Q3'],       // categories
  ['Revenue', 100, 112, 125],   // series 0
  ['Costs', 60, 66, 70],        // series 1
]
RuleDetail
Row 0Category labels (after the corner cell)
Later rowsOne series each; first cell is the series name
Cellsstring | number | boolean | null
Ragged rowsWarn, still load – pad when columns must align

Shared layout rules: chartData schema · seriesData.


Option bag: bar ​

clusteredBar, stackedBar, and stackedBar100 all read cd.bar. Stacking / 100% behaviour comes from chartType, not from a different bag.

FieldDefault (new charts)Role
padding0.3Gap between bars / clusters, 0–1
cornerRadius0Bar corner radius (px)
reverseStackOrderfalseStack order (mainly stacked types; harmless on clustered)
seriesStrokes / seriesStrokeWidths / seriesStrokeDashArrays[]Per-series stroke chrome
series[]Per-series / per-point override slots
overrides–Indexed style patches
js
{
  chartType: 'clusteredBar',
  seriesData: [/* … */],
  bar: {
    padding: 0.2,
    cornerRadius: 2,
  },
}

Axes & scales ​

RoleVertical (default)Horizontal
Domain (categories)bottomleft
Primary range (values)leftbottom
  • Keep 0 in range for honest bar lengths (engine bias for bars).
  • Format / tick interval / gridlines on the primary range side after orientation.
  • Secondary axis is uncommon on pure clustered bars; use combo or dual-axis line when units differ.

Details: Axes · by chart type · Value axis · Orientation.

Horizontal ​

js
{
  chartType: 'clusteredBar',
  orientation: 'horizontal',
  isDataTransposed: true,
  seriesData: [/* same layout */],
  title: { visible: true, text: 'Pageview Sources' },
  subtitle: { visible: false, text: '' },
  legend: { visible: false },
}

After the flip, put value formats and preferredTickInterval on axes.bottom, not axes.left. Live: Horizontal bar sample.


Type-specific behaviour ​

  • Palette lives on legend.colors, not on the bar bag.
  • Category totals default on. Hide them with annotations.totals.visible: false – see Labels. Do not invent showTotalLabels.
  • Data labels / series emphasis are mostly annotation and override trees – polish in the editor, then getChartData().
  • Changing to stackedBar / stackedBar100 keeps the same seriesData shape and the same bar bag; only the type (and axis % defaults for 100%) change.

Pitfalls ​

  • Using the line or area bag on a bar chart – rejected as foreign-option-bag
  • Formatting axes.left after setting orientation: 'horizontal' (range moved to bottom)
  • Pinning customMin above zero so bars look taller than the data
  • Expecting reverseStackOrder to change clustered side-by-side order in a meaningful way – it targets stacks
  • Wrong seriesData orientation (isDataTransposed: false) without matching the grid

Developer documentation for Chartbuddy Embed · Not the end-user Help Center · Help Center