caliper uidocs

Meter

Free

A measurement within a known range, with thresholds marked on the track and a word for whether it is fine.

Installs components/ui/meter.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/meter.json

Old preview builds are removed after 30 days.

CPU34%Healthy
Memory82%Getting high
p95 latency412 msSlow

When to use #

Use it for

  • Storage, quota, battery, a quality score or latency.
  • Values with a healthy band and a band to worry about.

Skip it when

  • Progress through a task; use progress.
  • Usage against a billed limit with segments; use the usage meter.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/meter.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above. It is built from Meter and meterState.

example.tsx
import { useState } from "react"
import { Meter } from "@/components/ui/meter"
import { Slider } from "@/components/ui/slider"

export function MeterDemo() {
  const [used, setUsed] = useState(71);
  return (
    <div className="grid w-full max-w-lg gap-6">
      <Meter
        label="Storage"
        value={used}
        max={100}
        low={80}
        high={95}
        optimum={0}
        format={(v) => `${v} GB of 100 GB`}
        words={{ good: "Plenty left", fair: "Filling up", poor: "Almost full" }}
        description={used >= 95 ? "Uploads stop at 100 GB. Delete old builds or add storage." : "Old preview builds are removed after 30 days."}
      />
      <div className="grid max-w-xs gap-1.5">
        <label htmlFor="meter-used" className="text-[13px] text-muted-foreground">Used storage</label>
        <Slider id="meter-used" min={0} max={100} value={used} onChange={(event) => setUsed(Number(event.target.value))} />
      </div>
      <Meter label="Battery" value={18} low={20} high={50} optimum={100} format={(v) => `${v}%`} words={{ poor: "Charge soon", fair: "Half way", good: "Charged" }} size="sm" />
      <div className="grid gap-4 border-t border-border pt-5 sm:grid-cols-3">
        <Meter variant="ring" label="CPU" value={34} low={70} high={90} optimum={0} />
        <Meter variant="ring" label="Memory" value={82} low={70} high={90} optimum={0} />
        <Meter variant="ring" label="p95 latency" value={412} max={500} low={200} high={350} optimum={0} format={(v) => `${v} ms`} words={{ poor: "Slow" }} />
      </div>
    </div>
  );
}

API reference #

Meter

component
PropTypeDefaultDescription
label *string—
value *number—
minnumber0
maxnumber100
lownumber—Top of the low band.
highnumber—Bottom of the high band.
optimumnumber—Where the best value lies; below `low` means lower is better, e.g. latency.
format((value: number) => string)(v) => `${Math.round(((v - min) / (max - min || 1)) * 100)}%`The value in words, e.g. (v) => `${v} GB of 100 GB`.
wordsPartial<Record<MeterState, string>>defaultWordsWhat each state is called, e.g. { poor: "Almost full" }.
showStatebooleantrueShow the state word beside the value.
descriptionReactNode—One line under the meter, e.g. what happens when it is full.
variant"bar" | "ring""bar"`ring` is a compact circular gauge for a dashboard row.
size"sm" | "md""md"
classNamestring—

meterState

component

No props of its own; it accepts the native props of its root element.

Accessibility #

It is built on native elements and keeps aria-hidden in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.