Gauge React chart component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React gauge chart showing one value against a scale and its zones: a speedometer arc, needle or segmented dial with a target tick.
A read-only meter, not focusable: nothing to tap or hover. The first time it scrolls into view the arc sweeps from the minimum on a soft spring (the needle turns, the segment marker rides the track) while the number counts up; later values glide from where they were, and the arc changes color as the value crosses into another zone. A long label or description that would not fit the bowl moves under the dial. Screen readers hear it as a meter with the formatted value, the zone and the target ("67.6%, Close zone, target 75%"). Drag the playground's slider to move the value. Reduced motion shows the final state at once.
Gauge 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 gauge, together with Chart kit, Motion presets, Number format, Tones, UIProvider and 1 more and the npm packages motion and d3-shape.
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 gaugeGauge 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 Gauge from @/components/ui/gauge.
import { Gauge } from "@/components/ui/gauge"
export function SalesGoal() {
return (
<Gauge
value={67.6}
valueFormat="percent"
target={75}
thresholds={[
{ value: 0, tone: "danger", label: "Behind" },
{ value: 50, tone: "warning", label: "Close" },
{ value: 75, tone: "success", label: "Good" },
]}
label="Monthly sales goal"
description="$6,760 of $10,000"
locale="en-US"
/>
)
}Props
value*default67.6number | nullThe current value. Outside min..max it is clamped for drawing, the text still shows the real number. null draws the track only and a dash.
mindefault0numberStart of the scale. min >= max renders the empty state (with a warning in development).
maxdefault100numberEnd of the scale.
variantdefault"arc""arc" | "needle" | "segments"arc fills the track up to the value; needle is a thin zone-colored track with a needle; segments cuts the track into zones (the current one solid, the rest at 25%, a ring marker at the value) or, with a segments count, into equal steps that light up.
sweepdefault"240"180 | 240 | 270Degrees of the dial, symmetric around 12 o'clock. 180 is a half circle, 270 an open ring.
sizedefault"md""sm" | "md" | "lg"Width 120 / 200 / 280px (the dial shrinks with a narrower container) and the value 18 / 28 / 40px.
thicknessdefault0numberTrack width in px. Auto is 10% of the width (12 / 20 / 28px), 4px for the needle track.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The arc color when no zone applies (no thresholds, colorByZone off, or a value below the first zone). neutral is the foreground ink.
colordefault""stringAny css color, a palette slot ("series-3") or a tone name. Replaces the tone; zones still win while colorByZone is on.
thresholds{ value: number; tone: Tone; label?: string }[]Zone starts, in any order (they are sorted): each zone runs from its value to the next one or max. Labels name the zone in the chip and the spoken text. Lower is better? List them success, warning, danger.
colorByZonedefaulttruebooleanThe arc and the chip take the tone of the zone the value is in.
showBandsdefaulttruebooleanA thin 3px zone band outside the arc (arc variant with thresholds); the zones the value is not in fade back.
segmentsdefault0numberEqual steps for the segments variant that light up to the value (tinted by their zone when there are thresholds). Auto is one segment per zone, or 10 steps without thresholds.
targetdefault0numberA tick across the track, cut into the surface so it reads over any color, and part of the spoken text. Hidden outside min..max. No default: without it there is no tick (the playground's 0 places it at 75 % of the range).
roundCapsdefaulttruebooleanRounded arc ends (segment corners get a 4px radius). Off gives square ends.
labeldefault""ReactNodeUnder the value, e.g. "Monthly goal". A string also names the meter; other nodes label it through aria-labelledby.
descriptiondefault""ReactNodeUnder the label in muted text, e.g. "$6,760 of $10,000". Linked with aria-describedby.
showValuedefaulttruebooleanThe big number in the bowl.
showMinMaxdefaulttruebooleanThe scale ends under the arc ends, compact for big numbers ("$190K").
showZonedefaulttruebooleanA small chip in the zone's tone with its label, when the current zone has one.
valueFormatdefault"number"ChartValueFormat ("number" | "compact" | "currency" | "percent")How the value and the scale ends print. percent takes whole numbers: 67.6 is "67,6 %".
currencydefault"USD"stringISO 4217 code for valueFormat="currency", with its narrow symbol ("$", "€").
decimalsdefault0numberFraction digits of the value. Unset (Auto in the playground) keeps the final value's digits all through the count (67.6 counts in tenths, 42 in whole numbers).
formatValue(value: number, context: { kind: "value" | "tick" | "label" }) => stringFull control over the text, e.g. v => `${v.toLocaleString("en-US")} h`. value is called on every frame of the count, tick for min / max, label for the spoken text.
localedefault""stringIntl locale for every number. Empty follows the UIProvider, then the page.
surfacedefault""stringThe color behind the gauge: the target notch and the segment marker are cut in it. Empty uses the surface token.
animateddefaulttruebooleanSweeps and counts in the first time it scrolls into view, then glides between values. Off renders the final state (thumbnails, print).
loadingdefaultfalsebooleanThe chart kit's gauge skeleton and a placeholder for the value; the label stays.
labels{ target?: string; zone?: (label: string) => string; valueText?: (value, min, max, info) => string; empty?: string; loading?: string }The spoken text. Defaults: "target", "{label} zone", "No data", "Loading"; valueText replaces the whole aria-valuetext (info has the formatted text, zone and target).
classNamesPartial<Record<"dial" | "svg" | "track" | "band" | "arc" | "segment" | "needle" | "marker" | "target" | "content" | "value" | "label" | "description" | "zone" | "min" | "max", string>>Extra classes for the parts.
classNamestringExtra classes for the root, merged last.
Parts
DEFAULT_GAUGE_LABELS
constantThe English strings: "target" and the zone suffix in the spoken text, plus "No data" and "Loading"; valueText is left to the built-in sentence.
Examples
NPS needle
min -100, max 100, four labelled zones and a needle that settles on 42.
Segments, lower is better
Support response time: zones run success, warning, danger; a marker at 3.2 h and a 4 h target.
Live updates
Each paid invoice glides the arc forward; past 75 % it turns green.
Sweeps
180, 240 and 270 degrees with the same value, zones and target.
Sizes in KPI cards
sm in list rows, md and lg in cards, currency with compact scale ends.
Neutral, brand and custom color
The monochrome default, brand orange with square ends, a palette slot on segments.
Loading and no data
The kit's gauge skeleton, and a null value that draws the track and a dash.
Files
- components/ui/gauge.tsx
FAQ
Is the React Gauge chart component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Gauge?
Run npx wingo-ui@latest add gauge. It also adds the Wingo UI items it builds on (Chart kit, Motion presets, Number format, Tones, UIProvider and 1 more) and the npm packages motion and d3-shape. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/gauge.
See also
More React chart and stat components
Articles using this component
- React Gauge Chart: Zones, Targets and Small Sizes
Component guides9 min read