← All components
data-display / stable

Meter

Single or stacked progress with semantic segment fills and hover, keyboard and tap breakdowns.

LIVE PREVIEW
Loading preview…

When to use

  • Known progress against a positive maximum.
  • Delivery states with fixed colors assigned by meaning.
  • Progress toward a goal that can be exceeded, with reference ticks.

Consider an alternative

  • Unknown scope — show No scope yet.
  • Indeterminate loading — use Spinner.
  • Compact record property — use ProgressPill.

Import

import { Meter } from '@revido-llc/kit';

Keyboard interaction

  • Tab — reveal the full breakdown
  • Escape — dismiss the tooltip
  • Touch — tap a segment to toggle its breakdown

Props

Props documented by the Kit. Refer to the TypeScript types for the complete interface, including inherited props.

label (required)string—
value (required)number—
classNamestring—
detailsReactNode—
markersreadonly MeterMark[]—Ticks on the scale, for example milestones beyond the target. Ids (or labels) must be unique.
maxnumber100Positive upper bound of the scale. When `target` is set and `max` is omitted, the scale extends to the largest of target, value, segment total and markers, so progress past the target never throws. An explicit max still throws on overflow.
projectedLabelstring"projected"Word after a projected segment's label, shown in parentheses. Pass a translation for non-English UIs.
remainderLabelstring"Not started"Name of the unfilled part; "Remaining" when `target` is set.
segmentsreadonly MeterSegment[]—
showHeaderbooleantrueShow the label and summary above the track.
showLegendbooleantrueShow a neutral-text legend with token swatches.
showMarkLabelsbooleanfalsePrint target and marker labels under the track (use at most 4 marks). Otherwise labels appear only in the tooltip and legend. A label that would overlap another at the current width is hidden (target first, then scale order); its tick stays, and every mark is still named in the tooltip, the legend (when shown) and accessible text.
size"sm" | "md""md"Fill thickness: sm = 4px, md = 8px. Hover targets remain 16px tall.
targetMeterMark—Goal line. Percentages in the summary, tooltip and breakdown are measured against it, so they can exceed 100%.
tooltipbooleantrueHover, keyboard and tap breakdown.
trailingReactNode—
valueFormat((value: number) => string)KitProvider locale number format, up to 6 fraction digitsFormats every number in the tooltip, legend and accessible text.
valueOfLabel((value: string, total: string) => string)(value, total) => `${value} of ${total}`Joins an amount and the total it is measured against, both already formatted. Pass a translation for non-English UIs.
valueTextstring—Summary shared by the visible header and assistive technology.

Examples

Single and stacked

<Meter label="Upload" value={65} valueText="13 of 20 files" />
<Meter label="Features" value={6} max={26} segments={[
  { id: "deployed", label: "Deployed", value: 4, tone: "success" },
  { id: "claimed", label: "Claimed", value: 2, tone: "warning" },
  { id: "in-progress", label: "In progress", value: 15, tone: "info" },
]} showLegend={false} />

With legend

A legend with token swatches under the track. valueFormat (here euros) formats every number; build it from the Kit locale inside a component, e.g. `const euros = useAmountFormatter({ format: { style: "currency", currency: "EUR", maximumFractionDigits: 0 } })`.

<Meter label="Delivery" value={4} max={10} segments={[{ id: "done", label: "Deployed", value: 4, tone: "success" }]} showLegend details="Verified delivery" />
// euros is a formatter in the Kit locale; create it inside your component:
// const euros = useAmountFormatter({ format: { style: "currency", currency: "EUR", maximumFractionDigits: 0 } });
<Meter label="Balance" value={1200} max={2000} valueFormat={euros} valueText={`${euros(1200)} of ${euros(2000)}`}
  segments={[{ id: "paid", label: "Paid", value: 1200, tone: "success" }, { id: "due", label: "Due", value: 450, tone: "warning" }]} />

Target and markers

Progress toward a goal line with ticks beyond it. Without max, the scale extends to the furthest mark; percentages are measured against the target.

<Meter label="Monthly goal" value={92} target={{ value: 110, label: "Target" }}
  markers={[{ value: 130, label: "Milestone 1" }, { value: 150, label: "Milestone 2" }]}
  segments={[{ id: "done", label: "Done", value: 92, tone: "primary" },
             { id: "planned", label: "Planned", value: 24, tone: "primary", variant: "projected" }]}
  valueText="92 of 110 · 18 to go" />

Over target

Values may pass the target; the summary can exceed 100%. An explicit max still rejects overflow.

<Meter label="Monthly goal" value={138} target={{ value: 110, label: "Target" }}
  markers={[{ value: 130, label: "Milestone 1" }, { value: 150, label: "Milestone 2" }]} showMarkLabels />

Sizes

<Meter label="Small" value={40} size="sm" />
<Meter label="Medium" value={40} size="md" />

Empty

<Meter label="Upload" value={0} max={20} valueText="0 of 20 files" />

Overflow guard

Values above max and overflowing segment totals are rejected with RangeError; validate data before rendering.

<Meter label="Complete" value={100} max={100} />

Related components