Bump chart
Free · MITRank-over-time lines that draw in with popping dots and names at the line ends; hovering or arrowing through periods and neighbours highlights one item and reads its change.
Installation
$ npx shadcn@latest add https://goodui.dev/r/bump-chart.jsonThe file lands in components/goodui/bump-chart.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/bump-chart works too.
Usage
import { BumpChart } from "@/components/goodui/bump-chart"
export function ServiceRanking() {
return (
<BumpChart
title="Most booked services"
periods={["May", "Jun", "Jul", "Aug", "Sep", "Oct"]}
series={[
{ key: "balayage", label: "Balayage", ranks: [2, 1, 1, 1, 2, 1] },
{ key: "fade", label: "Skin fade", ranks: [1, 2, 3, 2, 1, 2] },
{ key: "manicure", label: "Gel manicure", ranks: [3, 3, 2, 3, 3, 4] },
{ key: "massage", label: "Deep tissue", ranks: [5, 4, 4, 5, 4, 3] },
{ key: "brows", label: "Brow lamination", ranks: [4, 5, 5, 4, 5, 5] },
]}
rowHeight={40}
labelWidth={104}
/>
)
}When to use
- Who is first, second, third over time: most booked services by month, staff by revenue by week, plans by sign-ups.
- Stories about overtaking ("Balayage took the top spot in June") rather than exact values.
- Up to about six items; names sit at the line ends so no legend is needed.
- Exact values matter: use line-chart.
- Many items (10+): lines tangle; show a table with rank deltas instead.
- A single period: a ranked horizontal bar-chart is the right form.
| If you need | Use |
|---|---|
| Monthly service ranking | series={[{ key, label, ranks }]} periods={months} |
| Tighter rows | rowHeight={34} |
| No #1…#n column | rankLabels={false} |
| Longer names at the end | labelWidth={120} |
Examples
Weekly staff leaderboard
<BumpChart title="Staff by revenue" periods={weeks} series={staff} rowHeight={34} labelWidth={64} rankLabels={false} />API reference
| Prop | Type | Default |
|---|---|---|
| series* | BumpSeries[] | — |
| periods* | string[] | — |
| title Chart name for screen readers and the table caption. | React.ReactNode | — |
| rowHeight Vertical space per rank in px. | number | 36 |
| labelWidth Space reserved for the names at the end side, in px. | number | 88 |
| rankLabels "#1 … #n" on the start side. | boolean | true |
| strokeWidth | number | 2.5 |
| duration Seconds one line takes to draw; lines stagger by 0.1 s. | number | 1.1 |
| labels Text for built-in controls and screen readers; pass translations here. | Partial<BumpChartLabels> | — |
Also accepts every <div> attribute.
Accessibility
| Tab | Focuses the chart and highlights the first item at the last period. |
| ArrowLeft / ArrowRight | Previous or next period on the same item. |
| ArrowUp / ArrowDown | The item ranked just above or below at that period. |
| Home / End | First or last period. |
| Escape | Clears the highlight. |
- The chart is role="group" with aria-roledescription="bump chart" and a summary ("Most booked services: rank of 5 items over 6 periods").
- A screen-reader-only table lists each period with every item's rank.
- The highlighted point is announced as "<item>: #2 in Jun, up 1" through a polite live region.
- Names are printed at the line ends, so identity never relies on color; the rank axis is printed on the start side.
- Built-in strings (rank, up, down, same, summary, point, column header, noData) go through labels.
Motion
- On first view each line draws in from the first period with pathLength over duration (1.1 s, ease [0.65, 0, 0.35, 1]), 0.1 s apart; the dots pop on a spring (stiffness 420, damping 22) as the line reaches them and the names slide in at the end.
- Hovering a line, a dot or arrowing to a point fades the other items to 22% over 0.2 s; the active dot grows by 1.5px.
- Reduced motion: lines, dots and names appear at once.
- Each line is one cubic path plus a transparent 16px hit path for easy hovering; dots sit in motion groups translated once per layout.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- ranks are 1-based and one per period; the number of rows is the largest rank (at least the number of series).
- Lines are smooth S-curves between periods; there is no value axis, only rank.
- Colors default to --chart-1 … --chart-5 by series order; keep one color per entity across charts.
- The chart sizes its own height (rows × rowHeight + axis); the width follows the parent, with labelWidth reserved for names.
FAQ
Can an item be missing in some periods?
Leave the rank undefined at that index: the line skips the point. Ranks arrays shorter than periods simply end early.
Can I show values on hover?
The tooltip shows the rank and the change; attach values to your labels or use line-chart for values.
Related
Multi-series lines that draw in staggered, a legend that toggles series while the scale refits, a crosshair that reads every series at once, and a dashed forecast segment behind a divider.
Grouped, stacked or horizontal bars that grow from the baseline on view and morph when data changes, with a gliding hover band, legend toggles, value labels and 2px surface gaps between segments.
Shared interactive legend: aria-pressed toggle buttons with popping square, dot, line or dashed swatches, hidden-state strike-through, optional values and a hover channel that highlights a series in its chart.