- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Desktop Window
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Frame
- Heading
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Live Waveform
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Meter Ring
- Native Select
- Navigation Menu
- NumberCount
- Pagination
- Popover
- Progress
- Progress Ring
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Timeline
- Toast
- Toggle
- Toggle Group
- Tooltip
A radial reading of a bounded value, such as a score, a utilization, or a fill level.
import { MeterRing } from "@/components/ui/meter-ring"
export default function MeterRingDemo() {Four components cover this ground. MeterRing and
Meter report a value that simply is what it is —
a score, a utilization, a fill level;
Progress and
ProgressRing report a task climbing toward
completion. Pick the shape — ring or bar — second.
Installation#
bunx --bun shadcn@latest add @ondo-ui/meter-ring
Usage#
import { MeterRing, MeterRingLabel } from "@/components/ui/meter-ring"<MeterRing value={72} min={0} max={100} size="sm | default | lg" variant="default">
<MeterRingLabel>Health</MeterRingLabel>
</MeterRing>MeterRing builds on Base UI's Meter, so it is announced as a meter with the
right aria-valuenow and aria-valuetext. The dial always renders; anything you
pass as children sits under it, which is where MeterRingLabel goes.
format shapes the centered number. When the center needs something a number
format cannot express, formatValue replaces it outright.
The arc is a conic gradient masked into a ring — two elements, no SVG — and it
takes its color from currentColor, so variant (or any text-color utility in
className) recolors it.
When the reading changes the arc sweeps rather than jumping, because the
component registers its fraction with @property — that is what makes a number
inside a gradient interpolable. That registration ships inside the component
file — React hoists it into <head> once, however many rings are on the page —
so there is nothing to add to your CSS.
Examples#
Sizes#
import { MeterRing } from "@/components/ui/meter-ring"
export default function MeterRingSizes() {Variants#
variant picks the arc color from the theme's semantic set. Which band a
reading falls into is yours to decide, so pick the variant from the value at the
call site.
import { MeterRing, MeterRingLabel } from "@/components/ui/meter-ring"
export default function MeterRingVariants() {Formatting the value#
format takes Intl.NumberFormat options and shapes both the centered text and
the value announced to screen readers.
import { MeterRing, MeterRingLabel } from "@/components/ui/meter-ring"
export default function MeterRingFormat() {A center that isn't a number#
formatValue takes over the centered text. Reach for it when there is no reading
yet — a ring that shows 0.00 for "not scored" is stating something false — or
when the value is better read as a grade or an icon. The announced value is
unchanged, so the meter still reports the truth to assistive tech.
It is a function prop, so the component passing it has to be a Client Component.
"use client"
import { MeterRing, MeterRingLabel } from "@/components/ui/meter-ring"API reference#
MeterRing#
| Prop | Type | Default |
|---|---|---|
value | number | – |
min | number | 0 |
max | number | 100 |
size | "sm" | "default" | "lg" | "default" |
variant | "default" | "info" | "success" | "warning" | "destructive" | "default" |
format | Intl.NumberFormatOptions | – |
formatValue | (formatted: string, value: number) => ReactNode | – |
locale | Intl.LocalesArgument | runtime locale |
getAriaValueText | (formatted: string, value: number) => string | – |
Plus the rest of Base UI's Meter.Root props.
MeterRingDial, MeterRingTrack, MeterRingIndicator#
The ring and its parts. MeterRing composes them for you; reach for them only to
restyle the ring or to put something other than the value in the middle.
MeterRingValue, MeterRingLabel#
Base UI's Meter.Value and Meter.Label with ondo styling. MeterRingValue
takes a render function ((formatted, value) => ReactNode) when format alone
is not enough.