Drilldown

What is drilldown?

Drilldown turns a chart into a navigable hierarchy. Each data point can point to a child level, and clicking that point replaces the current view with the child's data. A breadcrumb trail tracks where you are and lets you step back up. It suits summary-to-detail stories: traffic by device down to device by OS, sales by year down to year by channel, a region map down to its cities.

Drilldown is opt-in and works with bar, column, pie, donut, treemap, and heatmap charts.

Enabling drilldown

Drilldown is a tree-shakeable feature. The full build (the default apexcharts import and the CDN / script-tag bundle) already includes it, so a <script>-tag page needs nothing extra. On the slim build, register it with a side-effect import:

import ApexCharts from 'apexcharts'
import 'apexcharts/features/drilldown'

See Tree Shaking for the slim-build setup. Once the feature is present, switch it on with drilldown.enabled.

Linking points to child levels

Two pieces wire a drilldown together:

  1. A data point declares which level it opens with a drilldown id: { x: 'Mobile', y: 55, drilldown: 'mobile' }.
  2. drilldown.series lists the child levels, each keyed by a matching id.

Because each point carries its own child id, pie and donut series must use the object data form ({ x, y, drilldown }) rather than a flat numeric array.

options = {
  chart: {
    type: 'donut',
    height: 420
  },
  series: [{
    data: [
      { x: 'Mobile', y: 55, drilldown: 'mobile' },
      { x: 'Desktop', y: 33, drilldown: 'desktop' },
      { x: 'Tablet', y: 12 }
    ]
  }],
  drilldown: {
    enabled: true,
    breadcrumb: {
      show: true,
      rootLabel: 'All Devices'
    },
    series: [
      {
        id: 'mobile',
        name: 'Mobile by OS',
        data: [
          { x: 'iOS', y: 30, drilldown: 'mobile-ios' },
          { x: 'Android', y: 23 },
          { x: 'Other', y: 2 }
        ]
      },
      {
        id: 'mobile-ios',
        name: 'iOS Versions',
        data: [
          { x: 'iOS 17', y: 18 },
          { x: 'iOS 16', y: 9 }
        ]
      },
      {
        id: 'desktop',
        name: 'Desktop by OS',
        data: [
          { x: 'Windows', y: 20 },
          { x: 'macOS', y: 10 }
        ]
      }
    ]
  }
}

A point without a drilldown id (like Tablet above) is a leaf: clicking it does nothing. Child levels can nest to any depth by giving their own points drilldown ids that reference further series entries (here Mobile opens mobile, whose iOS point opens mobile-ios).

Single-series and multi-series levels

A child level supplies its data one of two ways:

  • data for a single-series level, such as a pie slice breakdown or a plain column set.
  • series for a grouped or stacked level, passing a full multi-series array. Use this to drill from a total column into that period split by channel.
drilldown: {
  enabled: true,
  series: [
    {
      id: '2024',
      name: '2024 by Channel',
      series: [
        { name: 'Online', data: [{ x: 'Q1', y: 120 }, { x: 'Q2', y: 140 }] },
        { name: 'Retail', data: [{ x: 'Q1', y: 90 }, { x: 'Q2', y: 100 }] }
      ]
    }
  ]
}

A level can also override the chart type, axes, colours, and plotOptions applied while it is shown, so a column can drill into a pie, or a level can carry its own palette.

The breadcrumb shows the trail from the root level to the current one and lets the reader jump back to any ancestor. It is on by default. Configure it under drilldown.breadcrumb, or set drilldown.breadcrumb: false to hide it entirely and drive navigation yourself.

drilldown: {
  enabled: true,
  breadcrumb: {
    show: true,
    position: 'top-left',   // or 'top-right'
    separator: ' / ',
    rootLabel: 'All'
  }
}

Zoom-from-point animation

By default a drill re-renders the chart. Set drilldown.animation.zoomFromPoint to anchor the transition at the clicked point instead, so the child level unfolds outward from it and settles back on the way up.

drilldown: {
  enabled: true,
  animation: {
    zoomFromPoint: true,
    speed: 260
  }
}

Loading child data on demand

Inline series is not required. When a drillable point has no matching level, ApexCharts calls drilldown.onDrillDown and awaits the level you return, so you can fetch it from an API only when the reader opens it.

drilldown: {
  enabled: true,
  onDrillDown: async ({ point, seriesIndex, dataPointIndex }) => {
    const rows = await fetch(`/api/breakdown/${point.x}`).then((r) => r.json())
    return {
      id: point.drilldown,
      name: `${point.x} detail`,
      data: rows
    }
  }
}

If the resolver throws or rejects, the drillDownError event fires and the chart stays on the current level.

Programmatic navigation

The drilldown methods let you move levels without a click, for example from an external control or a deep link:

chart.drillDown('mobile')   // open a level by its id
chart.drillUp()             // step back one level
chart.drillToRoot()         // jump back to the top level

Each returns a promise that resolves once the transition finishes.

Events

Attach handlers under chart.events to react to navigation:

chart: {
  events: {
    drillDownStart: (info) => { /* before the transition begins */ },
    drillDownEnd: (info) => { /* after it completes */ },
    drillUp: (info) => { /* after stepping back up */ },
    drillDownError: (info) => { /* async resolver failed */ }
  }
}

drillDownStart, drillDownEnd, and drillUp receive { from, to, point, seriesIndex, dataPointIndex }, where from and to are level ids ('root' at the top). drillDownError receives { id, error }.

Reference and demos

The full option list is in the drilldown reference. See it running in the column, donut, treemap, and heatmap demos.