Skip to content

Secondary / dual axis ​

Use a secondary value axis when two series need independent scales (e.g. revenue in dollars vs growth in %). Same units on two axes is usually harder to read; prefer one axis when you can.

Secondary sits on:

OrientationSecondary side
verticalright
horizontaltop

Config shape ​

js
axes: {
  right: {
    id: 'secondaryRange',
    enabled: true,
    visible: true,
    boundSeries: [1], // original series indices (0-based)
    labels: {
      preferredTickInterval: 10,
      numberFormat: {
        mode: 'custom',
        customFormat: {
          prefix: '',
          postfix: '%',
          decimalPlaces: 0,
          forceDecimals: false,
          thousandSeparator: ',',
          decimalSeparator: '.',
          signDisplay: 'auto',
          scaleDown: 0,
        },
      },
    },
    axisTitle: { visible: true, text: 'Growth' },
  },
}
FieldRole
enabled / visibleTurn the secondary scale and chrome on
boundSeriesWhich original series indices use this scale (not filtered/display order)
labels.*Independent ticks, bounds, and formats from the primary range
breaksSupported on secondary the same way as primary – see Breaks

Series not listed in boundSeries stay on the primary range. Primary bounds exclude secondary-bound series so each scale fits its own data.


Line charts ​

Dual-axis is fully supported on line charts:

js
{
  chartType: 'line',
  isDataTransposed: true,
  seriesData: [
    ['', 'Q1', 'Q2', 'Q3', 'Q4'],
    ['Revenue', 120, 132, 141, 155],
    ['Margin %', 18, 19, 17, 21],
  ],
  axes: {
    left: {
      axisTitle: { visible: true, text: 'Revenue' },
      labels: {
        numberFormat: {
          mode: 'custom',
          customFormat: {
            prefix: '$',
            postfix: '',
            decimalPlaces: 0,
            forceDecimals: false,
            thousandSeparator: ',',
            decimalSeparator: '.',
            signDisplay: 'auto',
            scaleDown: 0,
          },
        },
      },
    },
    right: {
      id: 'secondaryRange',
      enabled: true,
      visible: true,
      boundSeries: [1],
      axisTitle: { visible: true, text: 'Margin %' },
      labels: {
        preferredTickInterval: 5,
        numberFormat: {
          mode: 'custom',
          customFormat: {
            prefix: '',
            postfix: '%',
            decimalPlaces: 0,
            forceDecimals: false,
            thousandSeparator: ',',
            decimalSeparator: '.',
            signDisplay: 'auto',
            scaleDown: 0,
          },
        },
      },
    },
  },
}

Live: Dual-axis line sample.

Indices in boundSeries match row order in seriesData after the header row – series 0 is the first data row, series 1 the second, and so on.


Horizontal dual axis (secondary on top) ​

With orientation: 'horizontal', put secondary on top:

js
{
  orientation: 'horizontal',
  chartType: 'line',
  axes: {
    bottom: { /* primaryRange – values */ },
    top: {
      id: 'secondaryRange',
      enabled: true,
      visible: true,
      boundSeries: [1],
    },
    left: { /* domain – categories */ },
  },
}

Give the chart enough height: domain labels on the left plus a top value axis need vertical room.


Combo charts ​

Combo can bind series to the secondary axis the same way (boundSeries + enabled / visible). Pair with combo.seriesTypes so the secondary series renders as a line (or whatever type you need):

js
{
  chartType: 'combo',
  isDataTransposed: true,
  seriesData: [
    ['', 'Q1', 'Q2', 'Q3', 'Q4'],
    ['Units', 40, 48, 52, 61],
    ['ASP', 12, 11, 13, 14],
  ],
  combo: {
    seriesTypes: { 1: 'line' },
  },
  axes: {
    right: {
      id: 'secondaryRange',
      enabled: true,
      visible: true,
      boundSeries: [1],
      labels: {
        numberFormat: {
          mode: 'custom',
          customFormat: {
            prefix: '$',
            postfix: '',
            decimalPlaces: 0,
            forceDecimals: false,
            thousandSeparator: ',',
            decimalSeparator: '.',
            signDisplay: 'auto',
            scaleDown: 0,
          },
        },
      },
    },
  },
}

Bars that stay on the primary axis still share one primary scale. Prefer exporting from the editor if you are unsure of series indices after reordering.


Anti-patterns ​

  • Two axes for the same unit (readers compare the wrong scale)
  • Forgetting enabled: true and visible: true
  • Wrong boundSeries index after inserting/reordering series rows
  • Putting secondary on right after flipping to horizontal (it moves to top)

Next: Value axis · Breaks · By chart type

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