Skip to content
WANCRAFT

G.59Data & Feedback

Bar Race

Horizontal bars that grow, shrink and overtake each other as the data changes.

NewReactMotionTailwindintermediate

Overview

Bar Race is a sorted horizontal bar chart built for data that changes over time. When new values arrive, rows reorder with a Motion layout animation, widths tween to their new share of the leader, and every value label counts from the number on screen to the new one — never from zero. Bars are tracked by id, so entrants fade in and fall-outs fade out as they cross the max-bars cut-off. The leader can take the accent colour while the field stays neutral, and a screen-reader table mirrors the full ranking while leader changes are announced politely.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/bar-race.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/bar-race.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/bar-race.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { BarRace, type BarRaceDatum } from "@/components/ui/bar-race";

export function BarRaceExample() {
  const data: BarRaceDatum[] = [
    { id: "kch", label: "Kuching", value: 1840 },
    { id: "kul", label: "Kuala Lumpur", value: 2210 },
    { id: "sin", label: "Singapore", value: 1975 },
    { id: "pen", label: "Penang", value: 1320 },
  ];

  return (
    <BarRace label="Projects shipped per studio" data={data} />
  );
}

03Props

PropTypeDefaultDescription
data{ id: string; label: string; value: number; color?: string }[]—Rows (required). Pass a new array to animate; ids keep bars stable.
labelstring—Accessible name and caption of the screen-reader table (required).
maxBarsnumber8Number of bars shown; the table lists every row.
durationnumber700Reorder, width and number tween length in ms.
notation"standard" | "compact""standard"Default Intl number notation.
format(value: number) => string—Custom value formatter (overrides notation).
highlightLeaderbooleantrueLeader in the accent colour, others neutral (bars without their own colour).
showRankbooleantrueShow rank numbers.
size"sm" | "md" | "lg""md"Row height.
accentstring"#FF7448"Leader / bar colour.
announcebooleantrueAnnounce leader changes via aria-live="polite".

04Features

  • 01Rows reorder with a Motion layout animation keyed by id
  • 02Widths tween to their share of the leader
  • 03Value labels count from the previous value, never from zero
  • 04Entrants and drop-outs fade at the max-bars cut-off
  • 05Leader highlight with a neutral field, or per-row colours
  • 06Standard or compact number notation, or a custom formatter
motion · layoutmotion · tweeninput · click

05Accessibility

  • The animated bars are aria-hidden; a visually hidden table (caption, column and row headers) lists the full ranking with formatted values.
  • Leader changes are announced through a polite, atomic live region (“Kuching takes the lead with 2,410.”); nothing is announced on first render.
  • With prefers-reduced-motion rows, widths and numbers update instantly.