Gauge

Free · MIT

Semi-circular gauge with Core-tone bands whose needle springs to the value with a small overshoot (or an arc that fills), a rolling readout and a band label pill, exposed as a meter.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/gauge.json

The file lands in components/goodui/gauge.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/gauge works too.

Usage

import { Gauge } from "@/components/goodui/gauge"

export function NoShowGauge({ rate }: { rate: number }) {
  return (
    <Gauge
      value={rate}
      min={0}
      max={20}
      label="No-show rate"
      bands={[
        { from: 0, to: 5, tone: "success", label: "Healthy" },
        { from: 5, to: 10, tone: "warning", label: "Watch" },
        { from: 10, to: 20, tone: "danger", label: "Critical" },
      ]}
      formatValue={(v) => `${v}%`}
    />
  )
}

When to use

Use it for
  • One value that lives inside ranges with meaning: no-show rate (healthy / watch / critical), chair utilization, NPS.
  • Dashboards where the reading should be felt as well as read: the needle swings and settles.
  • A score between a min and a max that is not 0..100 (rating 1..5).
Reach for something else
  • Plain progress toward a goal: use radial-progress or meter.
  • Several values side by side: a row of bars compares better than a row of dials.
  • Values without ranges: a stat tile with a delta is clearer (kpi-trend).
If you needUse
Status rangesbands with tone: success / warning / danger / info / neutral / primary and a label
Filled arc instead of a needlevariant="arc" (fills in the current band's color over a thin band ring)
Plain progress lookno bands, variant="arc", color
Percent valuesformatValue={(v) => `${v}%`}

Examples

Filled arc, utilization

<Gauge value={82} variant="arc" label="Chair utilization" bands={utilizationBands} formatValue={(v) => `${v}%`} />

Rating 1..5, no bands

<Gauge value={4.6} min={1} max={5} variant="arc" color="var(--chart-2)" label="Average rating" thickness={10} format={{ minimumFractionDigits: 1, maximumFractionDigits: 1 }} />

API reference

PropTypeDefault
value*number—
color
Color of the needle (needle) or of the arc when no band covers the value (arc).
string—
minnumber0
maxnumber100
bands
Colored ranges along the arc, e.g. healthy / watch / critical. Drawn with 2px gaps.
GaugeBand[][]
variant
needle: a needle springs to the value over the bands. arc: the arc fills to the value in the band's color.
"needle" | "arc""needle"
size
Width in px; the gauge is half as tall plus the readout.
number240
thickness
Arc thickness in px.
number14
label
Metric name under the value; also the accessible name when it is a string.
React.ReactNode—
showRange
Min and max printed under the arc ends.
booleantrue
format
Intl.NumberFormat options for the value, e.g. { style: "percent" } with values 0..1.
Intl.NumberFormatOptions—
localestring"en-US"
formatValue(value: number) => string—
labels
Text for built-in controls and screen readers; pass translations here.
Partial<GaugeLabels>—

Also accepts every <div> attribute.

Accessibility

  • The root is role="meter" with aria-valuenow, aria-valuemin, aria-valuemax and aria-valuetext ("6.4%, Watch"); the accessible name is the string label or labels.name.
  • Value changes are announced through a polite live region with the same text.
  • The current band is shown as a pill with its label and a color dot under the value, so the status is never color-only.
  • Band tones use the kit's Core tone colors with dark-mode variants; pass color on a band to override.

Motion

  • On first view the needle springs from the minimum to the value (stiffness 70, damping 11, mass 0.9), overshooting slightly before it settles; the arc variant fills on the same spring. The readout rolls with NumberFlow.
  • New values re-spring from the current angle.
  • Reduced motion: the needle and readout jump to the value.
  • The spring runs as a motion value; React re-renders with the rounded angle, which is cheap for an SVG of a dozen elements.

Notes for AI

Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.

  • Bands are [from, to] in value units and should tile the range from min to max without gaps; gaps between bands are drawn automatically (2px).
  • variant="needle" highlights the band the value is in and dims the others; variant="arc" draws the bands as a thin outer ring and fills the inner arc.
  • Give a string label so the meter has an accessible name; otherwise set labels.name.
  • size is the width in px; the SVG is half as tall plus the min/max line.

FAQ

Can the needle go past max?

The angle is clamped to the dial; the readout and aria-valuenow show the real value.

Why does the needle wobble before stopping?

That is the spring (damping 11). It is intentional and off under reduced motion.

Related