Tracker React chart component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React status tracker: a row of status blocks over time, one per day, hour or deploy, for uptime pages, sync health and habit streaks.
The first time the tracker scrolls into view its blocks rise from the bottom in one quick left-to-right sweep. Hover a block for a readout above the bar with its date, status and note; the block lifts and the others fade back (the nearest block answers, so thin blocks and gaps never flicker). Click a block to fire onBlockClick. On a phone the whole bar is the target: a tap pins the readout above it, a sideways drag scrubs through the days while a vertical drag still scrolls the page, a second tap on the same block opens it and a tap outside clears the readout. On narrow screens fit="latest" keeps only the newest blocks that fit and the start caption follows. With the keyboard, Tab lands on the bar (the newest block), Left / Right walk the blocks, Home / End jump to the ends, PageUp / PageDown move by ten, Enter or Space fire onBlockClick and Escape clears; a polite live region reads each block ("Degraded on August 14, 2026: Slow responses from the portal"). Screen readers get a summary of the counts per status and a table of every block. When a sliding window gains a block, the row glides one slot left and the new block rises in; status changes crossfade.
Tracker is a Pro React chart component in the React chart and stat components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add tracker, together with Chart kit, Date utils, useLocale, Motion presets, Tones and 1 more and the npm package motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add trackerTracker is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import Tracker from @/components/ui/tracker.
import { Tracker, type TrackerDatum } from "@/components/ui/tracker"
const data: TrackerDatum[] = [
{ date: "2026-09-18", status: "ok" },
{ date: "2026-09-19", status: "degraded", description: "Higher response times" },
{ date: "2026-09-20", status: "ok" },
{ date: "2026-09-21", status: "ok" },
{ date: "2026-09-22", status: "ok" },
{ date: "2026-09-23", status: "ok" },
{ date: "2026-09-24", status: "ok" },
]
export function EinvoicingStatus() {
return (
<Tracker
data={data}
label="Tax e-invoicing sync"
summary="99.82%"
locale="en-US"
statuses={{
ok: { label: "Operational", tone: "success" },
degraded: { label: "Degraded", tone: "warning" },
down: { label: "Down", tone: "danger" },
}}
onBlockClick={(datum) => console.log(datum.date)}
/>
)
}Props
data*TrackerDatum[] = { id?: string; date?: string; status: string; label?: ReactNode; description?: ReactNode }[]One entry per block, oldest first. date is an iso day or date-time (the tooltip title and captions), label replaces the date as the title ("Deploy #482"), description is the note under the status. id (or the date) keys the update animations.
statusesRecord<string, { label: string; tone?: Tone; color?: string }>Merged over the defaults key by key: ok (success, "Operational"), degraded (warning, "Degraded"), down (danger, "Outage"), maintenance (info, "Maintenance") and none (foreground at 10%, "No data"). Overriding only a label keeps the default tone and color ({ none: { label: "No data" } }). color takes any CSS color, a palette slot ("series-3") or a tone name and wins over tone. Your own keys come first in the legend. Unknown keys draw as none with a dev warning.
labeldefault""ReactNodeThe title at the start of the header; also names the figure for screen readers.
summarydefault""ReactNodeThe figure at the end of the header, e.g. the uptime.
sizedefault"md""sm" | "md" | "lg"Block height: 24 / 32 / 40px. On touch screens the tap target grows to at least 44px either way.
radiusdefault"sm""none" | "sm" | "md" | "full"Block corners: 0 / 2 / 3px, or pills.
gapdefault2numberPixels between blocks. With fit="all" it shrinks when the blocks would not fit otherwise.
fitdefault"latest""latest" | "all"latest: blocks keep minBlockWidth and the oldest drop off on narrow screens (the start caption follows). all: every block squeezes, down to 1px.
minBlockWidthdefault4numberThe narrowest a block gets with fit="latest", in px.
showCaptionsdefaulttruebooleanStart and end captions under the bar ("90 days ago", "Today"), from labels.start and labels.end.
showLegenddefaultfalsebooleanThe statuses present in the shown blocks, as keys with labels.
showTooltipdefaulttruebooleanThe readout on hover, tap and keyboard. The live region and the table still carry every block when off.
tooltipModedefault"fixed""auto" | "float" | "fixed"fixed pins the readout above the bar, centered on the block; float rides beside it; auto floats with a mouse and pins on touch.
onBlockClick(datum: TrackerDatum, index: number) => voidA click, a second tap on a pinned block, or Enter / Space on the inspected block. index is the position in data.
localedefault""stringLocale for the dates. Empty uses the UIProvider / LocaleProvider locale.
animateddefaulttruebooleanThe sweep on first view and the slide when blocks are appended. Reduced motion always shows the final state.
loadingdefaultfalsebooleanA pulsing skeleton in the bar's shape, plus the summary and captions.
emptyStateReactNodeReplaces the bar when data is empty. Default: the kit's empty state with labels.empty.
descriptiondefault""stringA one-sentence takeaway for screen readers, read before the counts per status.
labelsPartial<TrackerLabels>Strings: start(count, first), end (string or (last) => string), block(status, date), summary(counts, total), table, date, status, note, loading, empty, inspectHint. English by default ("90 days ago", "Today", "Degraded on 14 August 2026").
classNamesPartial<Record<TrackerSlot, string>>Classes for header, label, summary, bar, block, footer, legend, tooltip and table.
Parts
DEFAULT_TRACKER_STATUSES
constantThe built-in statuses that statuses merges over: ok ("Operational", success), degraded ("Degraded", warning), down ("Outage", danger), maintenance ("Maintenance", info) and none ("No data", foreground at 10%).
DEFAULT_TRACKER_LABELS
constantThe English strings: start ("90 days ago"), end ("Today"), block ("Degraded on" plus the date), summary, table, date, status, note, loading, empty and inspectHint. Spread it into labels to translate a few.
TrackerDatum
typeOne block: { id?, date?, status, label?, description? }. status is a key of statuses; label replaces the date in the tooltip ("Deploy #482").
TrackerStatus
typeOne status: { label, tone?, color? }. color takes any CSS color, a palette slot or a tone name and wins over tone.
TrackerLabels
typeThe shape of the labels object, with start, end, block and summary as functions.
Examples
Status page
Three integrations, each with its uptime.
Deliveries with a legend
Custom statuses, size lg and showLegend.
Sizes, radii and gaps
A habit streak in neutral ink, down to pill blocks.
Hourly
24 blocks for today with hour captions and tooltip titles.
Fit on a phone
fit="latest" vs fit="all" in 360px frames.
Live updates
A sliding window of deploys with onBlockClick.
Loading and empty
The bar-shaped skeleton and the empty state.
Files
- components/ui/tracker.tsx
FAQ
Is the React Tracker chart component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Tracker?
Run npx wingo-ui@latest add tracker. It also adds the Wingo UI items it builds on (Chart kit, Date utils, useLocale, Motion presets, Tones and 1 more) and the npm package motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/tracker.
How does Tracker behave on phones?
On a phone the whole bar is the target: a tap pins the readout above it, a sideways drag scrubs through the days while a vertical drag still scrolls the page, a second tap on the same block opens it and a tap outside clears the readout.
See also
Used in
More React chart and stat components
Articles using this component
- Nextjs Status Page: 90-Day Uptime Bars and Incidents
Build it: screens and apps12 min read