Unit Chart

Premium feature

The Unit chart is a Premium feature

Available on the Premium and OEM plans. It ships in the ApexCharts package as an opt-in import; add import 'apexcharts/unit' to enable it.

What is a Unit Chart?

A unit chart draws one discrete mark for every unit of value instead of a single bar or slice, so "37 of 200 seats" or "% of goal" reads as a countable quantity. It is a non-axis chart type (no grid or scale by default, dispatched like a pie or treemap) that covers a whole family of count-based visuals: dot clusters, pictograms (isotype charts), waffles, and beeswarms, all from one chart.type: 'unit'.

The signature behaviour is motion. On an update each mark tweens from its old position to its new one, so re-grouping, filtering, or a changing count re-forms the marks rather than redrawing from scratch.

When to Use a Unit Chart?

Use a unit chart when the count is the message and you want the quantity to feel tangible:

  • Part-to-whole where the whole is a countable population ("18 of 200 delegates abstained").
  • Pictograms that put a recognisable icon behind each unit (people, houses, cars).
  • A waffle or percentage grid as a friendlier alternative to a pie.
  • A beeswarm that shows every observation on a value axis without the dots overlapping.

When to avoid:

  • Very large counts where individual marks are no longer distinguishable. Use unitValue to let one mark stand for many, or reach for a bar chart.
  • Precise value comparison. Reading an exact number off a cluster of dots is slower than off an axis.

Quick Start

Flat counts with matching labels are the simplest input. Each value becomes that many marks, grouped into a cluster per category.


const chart = new ApexCharts(el, {
  chart: { type: 'unit' },
  series: [276, 266, 3],
  labels: ['For', 'Against', 'Abstain'],
  plotOptions: { unit: { layout: 'grouped' } },
})
chart.render()

The Layouts

plotOptions.unit.layout chooses how the marks are placed. One engine, seven built-in arrangements, plus a seam for your own.

Grouped and packed

'grouped' (the default) gives each category its own phyllotaxis blob, laid out in a row. 'packed' merges everything into one blob coloured by group, with sortByGroup nesting the minority in the centre, useful for a population with a highlighted subgroup.


plotOptions: {
  unit: {
    layout: 'grouped', // or 'packed'
  }
}

Workforce as a dot cluster

Columns

'columns' builds each category into a vertical bar of stacked dots, a unit column whose height encodes the count.

Unit columns

Grid (waffle)

'grid' fills one lattice of cells in category order, a part-to-whole square "pie". chart.type: 'waffle' presets this layout with square cells; see the waffle chart guide for grid.total percentage waffles and grid.split small multiples.

Scatter (beeswarm and 2D bubbles)

'scatter' places marks on real value axes. scatter.y: 'lanes' (the default) is a beeswarm: a value axis with one category lane per series, packing equal values off the lane centre so nothing overlaps. scatter.orientation picks which axis carries the value: 'horizontal' (the default) puts it on X with lanes stacked on Y, 'vertical' puts it on Y with lanes as columns across X. scatter.y: 'value' switches to a 2D value-value plot, and scatter.sizeRange turns the marks into area-scaled bubbles.


plotOptions: {
  unit: {
    layout: 'scatter',
    scatter: {
      y: 'value',          // 'lanes' (beeswarm) | 'value' (2D)
      sizeRange: [6, 44],  // area-scaled bubbles from datum.z
      xTitle: 'Income',
      yTitle: 'Cost of living',
    },
  },
}

Life expectancy beeswarm City bubbles, 2D scatter

Arc (parliament / hemicycle)

Added in ApexCharts 6.7, 'arc' arranges the marks as seats in concentric arced rows across an annulus, filled in category order so each category is a contiguous wedge. It is the classic parliament or hemicycle, the natural shape for seat counts and any part-to-whole where a semicircle reads better than a grid. Angles use the radialBar convention (0 at the top, clockwise); the default sweep is a top semicircle, and a full circle is startAngle: 0, endAngle: 360.


plotOptions: {
  unit: {
    layout: 'arc',
    arc: {
      startAngle: -90,       // top semicircle by default
      endAngle: 90,
      innerRadiusRatio: 0.4, // donut hole (inner / outer radius)
      rows: 'auto',          // seat rows, or a fixed number
    },
  },
}

Like the packed layout, the legend carries the category names (there are no per-wedge labels).

Custom (your own arrangement)

Every built-in arrangement is a closed set, so an arrangement none of them expresses used to need a change to the library. layout: 'custom' opens it:


plotOptions: {
  unit: {
    layout: 'custom',
    positions: (objects, rect) => objects.map((o, i) => ({
      id: o.id,
      x: rect.x + (i % 20) * 14,
      y: rect.y + Math.floor(i / 20) * 14
    }))
  }
}

A layout is objects in, positions out: it takes (objects, rect) and returns [{ id, x, y, r? }]. It knows nothing about animation, because the engine already tweens position, radius and colour and already keeps a mark's identity across a relayout. That is the whole point of the seam, a new arrangement needs no new transition code.

objects carries identity and data per mark rather than just an index, so you can place a specific unit rather than a positional slot. Marks whose ids you omit animate out; ids matching no mark are ignored.

Register a layout by name to reuse it across charts:


ApexCharts.registerUnitLayout('silhouette', (objects, rect) => [...])

// then
plotOptions: { unit: { layout: 'custom', positions: 'silhouette' } }

Unit Chart with a Custom Silhouette Layout

Tuning the Gather Animation

Every layout share the same gather tween that moves marks into place on an update. Since 6.7 it is tunable through plotOptions.unit.gather. easing picks the travel curve, 'outCubic' (the default) decelerates to a stop, 'inOutCubic' accelerates gently out of rest, and 'outBack' overshoots each mark past its slot and springs back for a per-mark settle. enter sets where a fresh or appearing mark animates from: 'burst' (fly out from the cluster centre), 'fade' (materialise in place), or 'rise' (fade in while drifting up into the slot).


plotOptions: {
  unit: {
    gather: {
      easing: 'outBack',  // 'outCubic' | 'inOutCubic' | 'outBack'
      enter: 'rise',      // 'burst' | 'fade' | 'rise'
    },
  },
}

Data Format

Two input shapes.

Flat counts (above) are the quick path: a number per category plus labels.

The object form gives every mark its own datum, so a mark can carry its own colour, position, size, and tooltip content. This is what the scatter layout and the identity transition need.


series: [{
  name: 'Cities',
  data: [
    { name: 'Zurich', x: 72000, y: 118, z: 1.4 },
    { name: 'Tokyo',  x: 40000, y: 83,  z: 37.4 },
    // one object = one mark
  ],
}]
Datum fieldUsed byMeaning
value / yall layoutsthe mark's numeric value (count weight, or the Y position in scatter)
xscatter 2Dthe mark's X position on the value axis
zbubblesthe size value when scatter.sizeRange is set (key configurable via scatter.sizeField)
name / labeltooltipthe mark's own label
fillColorall layoutsper-mark colour, overriding the category colour
ididentity tweenstable key so a specific mark persists across a regroup

Transitions

plotOptions.unit.transition decides which previous mark each new mark tweens from on an update.

  • 'group' (default) keeps each mark in its category; category-level enters and exits fade in and out.
  • 'flow' keys marks by global draw order, so the anonymous crowd migrates and recolours across a regroup, the circles-to-bars effect.
  • 'identity' keys by each datum's id / name, so a specific mark persists across any regroup or relayout, keeping its colour and size (needs the object form with unique ids).

Marks that leave fade out in place. The whole thing is a pure eased tween, with no physics simulation.

Pictograms (Isotype)

Set shape: 'image' to render an icon per unit. image.tint recolours a monochrome icon to the category colour so the pictogram matches the legend.


plotOptions: {
  unit: {
    shape: 'image',
    image: {
      src: '/icons/person.svg',
      width: 18,
      height: 18,
      tint: true,
    },
  },
}

Pictogram (isotype) unit chart

Bubble Sizing and Cluster Labels

sizeByValue scales each dot's radius by its per-unit value (circle shape, object-form data), with the lattice spaced for the largest bubble so nothing overlaps. clusterLabels adds a per-cluster label, a curved arc above a blob or a straight label above / below a bar.


plotOptions: {
  unit: {
    sizeByValue: { enabled: true, scale: 'area' },
    clusterLabels: { show: true, curved: true },
  },
}

Scaling and Safety

For large counts, unitValue lets one mark stand for many (e.g. unitValue: 1000 draws one dot per thousand), and maxUnits (default 5000) caps the total, scaling counts down proportionally above it.

Licensing

The unit chart is the first Premium chart type. Without a license it renders in trial mode with an APEXCHARTS watermark; a valid license key removes it. Every other chart type stays free. See the pricing page for the current terms.

Tree-Shaking

Unit is a tree-shakeable chart type. When you import ApexCharts via its module entry, pull in only the unit renderer, which also serves the waffle alias:

import ApexCharts from 'apexcharts/core'
import 'apexcharts/unit' // dot / pictogram / waffle / beeswarm

The full list of unit-specific settings is documented under plotOptions.unit.