Comparison · Intermediate

Bar race

Horizontal bars ranked at each time step that move to their new length and place as the steps play.

Open in playground
Use when
Telling how a ranking changed over time in a talk or a video, such as sales by brand per year
Avoid when
Readers need to compare exact values or ranks across time (a bump chart or a line chart does that better), or there is only one time step

Try it

39

Bar race

Yearly sales by brand (thousand units)
Bar raceBar race chart. 12 items ranked over 12 frames, from 2015 to 2026. At 2026, Lumen leads with 900.2026Lumen900Aurora663Halcyon593Vertex512Pinnacle501Northwind493Kestrel471Quill433
2015
2026
Bar race data
Lumen900, 1st
Aurora663, 2nd
Halcyon593, 3rd
Vertex512, 4th, up 3 places
Pinnacle501, 5th
Northwind493, 6th, down 2 places
Kestrel471, 7th, down 1 place
Quill433, 8th, up 1 place
Lumen climbs to 1stIntermediate

Hover over the chart, or Tab in and use the arrow keys to move between items. The numbers are sample data.

Variants

Named looks for this chart. Each one is the example above with a few options or fields changed. The CLI (ztd-charts example barRace --variant <id>) and the MCP server know the same names.

Top 5

Yearly sales by brand (thousand units)
Sample data

First year

Yearly sales by brand (thousand units)
Sample data

Without highlight

Yearly sales by brand (thousand units)
Sample data

Code

BarRace.tsx
import { ChartCard, BarRace } from "ztd-charts";

const data = [
  {"brand":"Northwind","year":2015,"sales":420},
  {"brand":"Aurora","year":2015,"sales":380},
  {"brand":"Kestrel","year":2015,"sales":350},
  {"brand":"Halcyon","year":2015,"sales":300},
  {"brand":"Lumen","year":2015,"sales":120},
  {"brand":"Orbit","year":2015,"sales":260},
  // … 138 more rows
];

export function Example() {
  return (
    <ChartCard number="39" title="Bar race" subtitle="Yearly sales by brand (thousand units)" takeaway="Lumen climbs to 1st" level="intermediate" replay>
      <BarRace
        data={data}
        label="brand"
        value="sales"
        frame="year"
        top={8}
      />
    </ChartCard>
  );
}

Props

<BarRace> takes these props. Rows marked Common work the same on every chart.

30 props

Prop
dataRequiredAn array of objects. Pass CSV, REST or database rows as they areobject[]
labelRequiredItem name fieldstring | (d, i) => string
valueRequiredValue fieldstring | (d, i) => number
frameRequiredTime step field: a year, a date or any ordered key. Numbers and dates play in order, other text in the order it first appears. An item with no value in a step leaves the bars for that stepstring | (d, i) => any
topBars in each step: the items with the largest valuesnumberDefault 10
stepMsTime per step while playing (ms)numberDefault 1000
autoplayPlay from the first step when the chart scrolls into view. Off by default so a page does not move by itself; it never plays with reduced motion, where the controls step one at a timebooleanDefault false
startFrameThe step shown first, and the one images and the first server paint show, as a step value such as 2021. Default: the last stepany
aggregateHow rows with the same step and label combine: "sum" adds them, "mean" averages, "last" keeps the last row, "count" counts rows"sum" | "mean" | "last" | "count"Default "sum"
heightCommonHeight in px. Without it, each chart picks a default that fits its widthnumber
widthCommonFixed width in px. Without it, the chart fits its parentnumber
highlightCommonItem to highlight. An index, a name, an array, or "max" | "min" | "first" | "last" | "none"number | string | (number|string)[] | "max" …
formatCommonValue format. "number" | "integer" | "percent" (values are already percents: 42 → "42%") | "ratio" (0.42 → "42%") | "compact" | "currency" (KRW for Korean locales, otherwise USD; for another currency pass Intl options with currency) | "decimal" or Intl.NumberFormat options"number" | "percent" | "compact" | …
unitCommonUnit appended to values. For example "%" or "ms"string
localeCommonLocale for numbers, dates and the text a chart writes itself (tooltips, screen reader sentences). "ko" or "ko-*" switches to Korean. Falls back to the ThemeProvider localestringDefault "en-US"
animateCommonEntry and update animationbooleanDefault true
durationCommonAnimation length (ms)number
easingCommonMotion curve"spring" | "easeOut" | "easeInOut" | "linear"Default "spring"
staggerCommonDelay between items as they enter (ms)number
reducedMotionCommon"auto" follows the OS setting. true always turns motion offanyDefault "auto"
animateOnViewCommonPlay when the chart scrolls into viewbooleanDefault true
showLabelsCommonLabels drawn on the chart (values and names)booleanDefault true
tooltipCommonShow tooltips. { template: "{label} · {value} · owner {owner}" } writes them: {label}, {value}, {series} or any other column of the row. \n starts a new line, and a line with an empty value is left outtooltipDefault true
syncIdCommonCharts with the same syncId show the hovered x together (line, area, bar, candlestick). <ChartGrid sync> sets one for every chart inside itstring
ariaLabelCommonName for screen readersstring
descriptionCommonDescription for screen readers. Built from the data when omittedstring
onHoverCommonRuns when hover or keyboard focus moves to another item(e: ChartEvent | null) => void
onClickCommonRuns on click, Enter or Space(e: ChartEvent) => void
replayKeyCommonReplays the entry animation when it changesstring | number
themeCommonChanges theme tokens for this chart onlyPartial<ChartTheme>