---
url: https://chartbuddy.io/embed/docs/chart-types/combo.md
---
# Combo

Mixed series types on one chart (bars + line/area) via the `combo` bag. Bind series to the [secondary axis](/axes/secondary) when units differ.

## Identity

| | |
|---|---|
| `chartType` | `combo` |
| Option bag | `combo` |
| `seriesData` layout | `seriesRows` – row 0 = category header; later rows = series |
| Min grid | 2 rows × 2 columns (header included) |
| Orientation | Yes – `vertical` (default) or `horizontal` (secondary moves to **top**) |
| Sample | [Combo](/samples/combo) · [Dual-axis line](/samples/dual-axis-line) |

***

## Minimal `chartData`

```js
{
  chartType: 'combo',
  isDataTransposed: true,
  seriesData: [
    ['', 'Q1', 'Q2', 'Q3', 'Q4'],
    ['Units', 40, 48, 52, 61],
    ['ASP', 12, 11, 13, 14],
  ],
  combo: {
    seriesTypes: { 1: 'line' },
  },
  title: { visible: true, text: 'Units and ASP' },
  subtitle: { visible: false, text: '' },
}
```

***

## `seriesData`

Same `seriesRows` layout as clustered bar / line (`isDataTransposed: true`). Series index in `combo.seriesTypes` and `boundSeries` is the data-row index among series (0 = first series row after the header).

```js
[
  ['', 'Q1', 'Q2', 'Q3', 'Q4'],
  ['Units', 40, 48, 52, 61], // series 0 – default bar
  ['ASP', 12, 11, 13, 14],   // series 1 – map to line
]
```

Shared layout rules: [chartData schema · seriesData](/api/chart-data#seriesdata-layouts).

***

## Option bag: `combo`

| Field | Default (new charts) | Role |
|---|---|---|
| `seriesTypes` | `{}` | Map **series index →** `'bar'` | `'line'` | `'area'` |
| `baseChartType` | `null` | Optional `'clusteredBar'` | `'stackedBar'` for unlisted / bar series |

Unlisted series default to bars. Stroke / fill detail still comes from the usual `line` / `area` / `bar` bags when those series render as that type – set `seriesTypes` first, then style.

```js
combo: {
  seriesTypes: { 0: 'bar', 1: 'line' },
  baseChartType: 'clusteredBar',
}
```

***

## Axes & scales

| Role | Vertical | Horizontal |
|---|---|---|
| Domain | `bottom` | `left` |
| Primary range | `left` | `bottom` |
| Secondary range | `right` | `top` |

Bars on the primary scale share one scale. Bind line/area series with different units via `boundSeries` on the secondary side:

```js
{
  chartType: 'combo',
  isDataTransposed: true,
  seriesData: [/* … */],
  combo: { seriesTypes: { 1: 'line' } },
  axes: {
    right: {
      id: 'secondaryRange',
      enabled: true,
      visible: true,
      boundSeries: [1],
      labels: {
        numberFormat: {
          mode: 'custom',
          customFormat: {
            prefix: '$',
            postfix: '',
            thousandSeparator: ',',
            decimalSeparator: '.',
            decimalPlaces: 0,
            forceDecimals: false,
            signDisplay: 'auto',
            scaleDown: 0,
          },
        },
      },
    },
  },
}
```

Details: [Axes · Secondary](/axes/secondary) · [Axes · by chart type](/axes/by-chart-type) · [Orientation](/concepts/orientation).

***

## Type-specific behaviour

* For a dual-scale **line-only** chart, `chartType: 'line'` + secondary is enough – combo is for mixed geometries.
* After inserting/reordering series rows, re-check `seriesTypes` and `boundSeries` indices.
* Prefer editor + `getChartData()` when unsure of indices.

***

## Pitfalls

* Wrong `seriesTypes` values (`'clusteredBar'` is not valid here – use `'bar'` | `'line'` | `'area'`)
* Enabling secondary without `boundSeries` (or with a stale index)
* Putting secondary on `right` after flipping to horizontal (it moves to `top`)
* Two axes for the same unit (readers compare the wrong scale)

***

## Related

* [Line](/chart-types/line) · [Clustered bar](/chart-types/clustered-bar) · [Axes · Secondary](/axes/secondary)
* [chartData schema](/api/chart-data)
* Samples: [Combo](/samples/combo) · [Dual-axis line](/samples/dual-axis-line)
