--- url: https://chartbuddy.io/embed/docs/chart-types/clustered-bar.md --- > Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt) # Clustered bar Side-by-side bars that compare series within each category. ## Identity | | | |---|---| | `chartType` | `clusteredBar` | | Option bag | `bar` | | `seriesData` layout | `seriesRows` – row 0 = category header; later rows = series | | Min grid | 2 rows × 2 columns (header included) | | Orientation | Yes – `vertical` (default) or `horizontal` | | Sample | [Clustered bar](https://chartbuddy.io/embed/docs/samples/clustered-bar.md) · [Horizontal bar](https://chartbuddy.io/embed/docs/samples/horizontal-bar.md) | *** ## 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 ] ``` | Rule | Detail | |---|---| | Row 0 | Category labels (after the corner cell) | | Later rows | One series each; first cell is the series name | | Cells | `string \| number \| boolean \| null` | | Ragged rows | Warn, still load – pad when columns must align | Shared layout rules: [chartData schema · seriesData](https://chartbuddy.io/embed/docs/api/chart-data.md#seriesdata-layouts). *** ## Option bag: `bar` `clusteredBar`, `stackedBar`, and `stackedBar100` all read **`cd.bar`**. Stacking / 100% behaviour comes from `chartType`, not from a different bag. | Field | Default (new charts) | Role | |---|---|---| | `padding` | `0.3` | Gap between bars / clusters, `0–1` | | `cornerRadius` | `0` | Bar corner radius (px) | | `reverseStackOrder` | `false` | Stack 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 | Role | Vertical (default) | Horizontal | |---|---|---| | Domain (categories) | `bottom` | `left` | | Primary range (values) | `left` | `bottom` | * 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](https://chartbuddy.io/embed/docs/chart-types/combo.md) or [dual-axis line](https://chartbuddy.io/embed/docs/samples/dual-axis-line.md) when units differ. Details: [Axes · by chart type](https://chartbuddy.io/embed/docs/axes/by-chart-type.md) · [Value axis](https://chartbuddy.io/embed/docs/axes/value-axis.md) · [Orientation](https://chartbuddy.io/embed/docs/concepts/orientation.md). ### 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](https://chartbuddy.io/embed/docs/samples/horizontal-bar.md). *** ## 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](https://chartbuddy.io/embed/docs/configuration/labels.md). 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 *** ## Related * [Stacked bar / 100%](https://chartbuddy.io/embed/docs/chart-types/stacked-bar.md) · [Combo](https://chartbuddy.io/embed/docs/chart-types/combo.md) * [chartData schema](https://chartbuddy.io/embed/docs/api/chart-data.md) · [Axes](https://chartbuddy.io/embed/docs/axes.md) · [Legend](https://chartbuddy.io/embed/docs/configuration/legend.md) * Samples: [Clustered bar](https://chartbuddy.io/embed/docs/samples/clustered-bar.md) · [Horizontal bar](https://chartbuddy.io/embed/docs/samples/horizontal-bar.md) · [Currency ticks](https://chartbuddy.io/embed/docs/samples/currency-ticks.md)