Waterfall Chart
What is a waterfall chart?
A waterfall chart shows how a starting value becomes an ending value through a sequence of signed steps. Each step is drawn as a column floating between the level the running total started at and the level it left behind, so the reader follows a walk from one figure to another: revenue down to operating income, opening headcount to closing headcount, last quarter's ARR to this quarter's.
ApexCharts added chart.type: 'waterfall' in 7.1.0. The series holds the deltas and the chart does the accumulation:
var options = {
chart: {
type: 'waterfall',
height: 380
},
series: [{
name: 'Operating income',
data: [
{ x: 'Net revenue', y: 8786000 },
{ x: 'Cost of sales', y: -2786000 },
{ x: 'Gross profit', isSubtotal: true },
{ x: 'Operating expenses', y: -1786000 },
{ x: 'Operating income', isTotal: true }
]
}]
}
var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()
A complete example is on this page.
When to use a waterfall chart?
- Bridging two figures: a P&L walk, a budget-to-actual variance, a period-over-period reconciliation.
- Showing the composition of a change, where a stacked bar would show the composition of a level instead.
- Any explanation of the form "we started here, these things happened, we ended there".
When to avoid:
- For parts of a whole at one point in time. That is a stacked bar or a pie.
- For more than about a dozen steps. The connectors and the floating columns stop reading as one walk.
- When the steps have no meaningful order. A waterfall's left-to-right sequence is a claim, and the staggered reveal animation traces it.
Data format
One series. Each datum is either a step or a running total.
| Datum | Carries | Draws |
|---|---|---|
| Step | y, the signed amount it moves the running total by | A column from the previous level to the new one |
| Subtotal | isSubtotal: true, no y | A column from the last cut to the running total, and starts a new cut |
| Total | isTotal: true, no y | A column from zero to the running total, and starts a new cut |
data: [
{ x: 'Net revenue', y: 8786000 },
{ x: 'Cost of sales', y: -2786000 },
{ x: 'Gross profit', isSubtotal: true }, // sum of the steps since the last cut
{ x: 'Operating expenses', y: -1786000 },
{ x: 'Amortisation', y: -453000 },
{ x: 'Income from equity', y: 1465000 },
{ x: 'Operating income', isTotal: true } // sum of every step from zero
]
You never supply the running total, an open value, or a [start, end] pair. Supplying y on a flagged datum is unnecessary: a subtotal or total bar is measured for you.
{x, y} objects, [x, y] tuples, and bare numbers alongside xaxis.categories all work, though a waterfall is normally written as {x, y} because the steps are named.
isSubtotal vs isTotal
Both draw the running total; they differ in where the bar starts and what "the last cut" means afterwards.
isSubtotalspans from the previous cut to the running total. It is the sum of the steps since the last flagged bar, so a second subtotal further down reports only the steps between the two.isTotalspans from zero to the running total. It is the sum of every step so far.
Both start a new cut, so the steps that follow accumulate from there.
What do the labels and tooltip report?
Every bar reads out end - start, its own signed height. That is the delta for a step bar and the accumulated figure for a subtotal or total bar, which is the one rule that makes all three read correctly without a special case.
dataLabels is on by default for a waterfall, since the step is the whole point. The labels get a pale chip with dark ink rather than the range column's white text: small steps are normal in a waterfall, and a label wider or taller than its bar gets placed outside it, where white text would land on the chart background and vanish.
dataLabels: {
formatter: function (val) {
return (val > 0 ? '+' : '') + val.toLocaleString('en-US')
}
}
Colours and connectors
Rising, falling, and running-total bars are coloured by what they mean. Under plotOptions.waterfall, the colors.positive and colors.negative keys default to green and red, because up-is-good is the one convention a waterfall is read by. The isSubtotal and isTotal bars default to the series colour from the active palette, so they stay distinct from the steps and still follow the theme.
A datum's own fillColor always wins over all four.
plotOptions: {
waterfall: {
colors: {
positive: '#00A86F',
negative: '#FF4560',
subtotal: '#008FFB',
total: '#008FFB'
},
connectors: {
show: true,
strokeDashArray: 3
}
}
}
The connectors are the dashed segments joining each bar's finish to the next one's start. Without them the floating columns read as unrelated bars rather than one walk, so they are on by default. Their colour falls back to grid.borderColor, which keeps them theme-aware. Bars are drawn at 60% of the slot rather than the usual 70%, because the gaps are load-bearing here: the connectors are drawn in them.
Horizontal waterfall
Set plotOptions.bar.horizontal: true for a left-to-right walk down the rows. Useful when the step names are long.
chart: { type: 'waterfall' },
plotOptions: {
bar: { horizontal: true }
}
Defaults worth knowing
- No legend. A waterfall is one series, so the series legend would show a single swatch named after it, and clicking that swatch empties the chart. The legend a waterfall actually wants names the kinds of bar (increase, decrease, total), which the series legend cannot express.
- No zoom. A waterfall is a fixed set of named steps, not a window onto a continuum. Without this a horizontal waterfall would inherit the timeline range bar's zoom toolbar.
- Staggered reveal on. The bars are a left-to-right sequence, so
animations.animateGraduallytraces the walk. A plain timeline range bar turns this off; a waterfall wants it. - Stacking forced off. The bars are already a cumulative walk, so
chart.stackedis ignored.
Tree-shaking
A waterfall bar is a float between two levels, which is what the vertical range column renderer already draws, so waterfall needs no renderer of its own. Its own entry point pulls in that renderer plus the accumulation and the connectors:
import ApexCharts from 'apexcharts/waterfall'
Or, alongside an existing bar import:
import ApexCharts from 'apexcharts/core'
import 'apexcharts/bar'
import 'apexcharts/features/waterfall'
The default apexcharts bundle already includes it. See tree-shaking for the full picture.
The complete list of waterfall settings is documented under plotOptions.waterfall.

