Meter
A graphical display of a numeric value within a range. Built with Base UI and Tailwind CSS. Copy-paste ready.
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
export default function Particle() {
return (
<Meter value={75}>
<div className="flex items-center justify-between gap-2">
<MeterLabel>Storage usage</MeterLabel>
<MeterValue />
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
);
}
Installation
pnpm dlx shadcn@latest add @coss/meter
Usage
import { Meter, MeterLabel, MeterValue } from "@/components/ui/meter"<Meter value={40}>
<MeterLabel>Progress</MeterLabel>
<MeterValue />
</Meter>API Reference
Meter
Root component. Styled wrapper for Meter.Root from Base UI with flex column layout.
MeterTrack
Track container for the meter indicator. Styled wrapper for Meter.Track from Base UI.
MeterIndicator
Visual indicator showing the current value. Styled wrapper for Meter.Indicator from Base UI.
MeterLabel
Label text for the meter. Styled wrapper for Meter.Label from Base UI.
MeterValue
Displays the current value. Styled wrapper for Meter.Value from Base UI.
Examples
With Label
Renders a MeterLabel above the track to describe what the meter measures (e.g. "Storage used").
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
} from "@/components/ui/meter";
export default function Particle() {
return (
<Meter value={50}>
<MeterLabel>Rating</MeterLabel>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
);
}
With Formatted Value
Uses MeterValue with a custom formatter to display the current value as a percentage or human-readable string.
"use client";
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
export default function Particle() {
return (
<Meter max={5} value={3}>
<div className="flex items-center justify-between gap-2">
<MeterLabel>Rating</MeterLabel>
<MeterValue>{(_formatted, value) => `${value} / 5`}</MeterValue>
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
);
}
With Range
Sets explicit min and max values to represent a bounded measurement, such as available disk space or quota usage.
"use client";
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
export default function Particle() {
return (
<Meter max={1000} min={500} value={700}>
<div className="flex items-center justify-between gap-2">
<MeterLabel>Bandwidth (Mbps)</MeterLabel>
<MeterValue>{(_formatted, value) => value}</MeterValue>
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
);
}
Stacked Resource Meters
Three labeled meters rendered in a vertical list — CPU, Memory, and Disk — each showing live percentage values for a system dashboard.
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
const resources = [
{ label: "CPU", value: 42 },
{ label: "Memory", value: 68 },
{ label: "Disk", value: 91 },
];
export function Pattern() {
return (
<div className="w-full max-w-sm space-y-4">
{resources.map(({ label, value }) => (
<Meter key={label} value={value}>
<div className="flex items-center justify-between gap-2">
<MeterLabel>{label}</MeterLabel>
<MeterValue />
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
))}
</div>
);
}
Color-Coded by Severity
MeterIndicator receives a dynamic className (green / amber / destructive) based on thresholds, giving instant visual feedback on metric health.
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
function indicatorColor(value: number) {
if (value >= 80) return "bg-destructive";
if (value >= 50) return "bg-amber-500";
return "bg-green-500";
}
const metrics = [
{ label: "Error rate", value: 12 },
{ label: "Latency", value: 63 },
{ label: "Memory", value: 88 },
];
export function Pattern() {
return (
<div className="w-full max-w-sm space-y-4">
{metrics.map(({ label, value }) => (
<Meter key={label} value={value}>
<div className="flex items-center justify-between gap-2">
<MeterLabel>{label}</MeterLabel>
<MeterValue />
</div>
<MeterTrack>
<MeterIndicator className={indicatorColor(value)} />
</MeterTrack>
</Meter>
))}
</div>
);
}
Storage Breakdown with Icons
Each meter row has a small icon prefix in the MeterLabel and a custom MeterValue formatter showing used / total GB instead of the default percentage.
"use client";
import { DatabaseIcon, FileIcon, ImageIcon } from "lucide-react";
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
const breakdown = [
{ icon: ImageIcon, label: "Photos", total: 10, used: 4.2 },
{ icon: FileIcon, label: "Documents", total: 10, used: 1.8 },
{ icon: DatabaseIcon, label: "Backups", total: 10, used: 7.5 },
];
export function Pattern() {
return (
<div className="w-full max-w-sm space-y-4">
{breakdown.map(({ icon: Icon, label, used, total }) => (
<Meter key={label} max={total} value={used}>
<div className="flex items-center justify-between gap-2">
<MeterLabel className="flex items-center gap-1.5">
<Icon
aria-hidden="true"
className="size-3.5 text-muted-foreground"
/>
{label}
</MeterLabel>
<MeterValue>{() => `${used} / ${total} GB`}</MeterValue>
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
))}
</div>
);
}
Upload Progress Card
A bordered card with a header icon, descriptive sub-label ("3 of 5 files complete"), and a single meter showing batch upload progress.
import { UploadCloudIcon } from "lucide-react";
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
export function Pattern() {
return (
<div className="w-full max-w-xs rounded-lg border p-4">
<div className="mb-3 flex items-center gap-2">
<UploadCloudIcon
aria-hidden="true"
className="size-4 text-muted-foreground"
/>
<span className="font-medium text-sm">Uploading files…</span>
</div>
<Meter value={63}>
<div className="flex items-center justify-between gap-2">
<MeterLabel className="text-muted-foreground text-xs">
3 of 5 files complete
</MeterLabel>
<MeterValue />
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
</div>
);
}
Profile Strength Checklist
A meter paired with a completion checklist — finished steps are struck through in muted text, remaining steps are highlighted, giving users a clear path to 100%.
- Add profile photo
- Set display name
- Verify email
- Connect calendar
- Invite a teammate
"use client";
import {
Meter,
MeterIndicator,
MeterLabel,
MeterTrack,
MeterValue,
} from "@/components/ui/meter";
const steps = [
{ done: true, label: "Add profile photo" },
{ done: true, label: "Set display name" },
{ done: true, label: "Verify email" },
{ done: false, label: "Connect calendar" },
{ done: false, label: "Invite a teammate" },
];
const completed = steps.filter((s) => s.done).length;
export function Pattern() {
return (
<div className="w-full max-w-xs space-y-3 rounded-lg border p-4">
<Meter max={steps.length} value={completed}>
<div className="flex items-center justify-between gap-2">
<MeterLabel className="font-medium">Profile strength</MeterLabel>
<MeterValue>{() => `${completed}/${steps.length}`}</MeterValue>
</div>
<MeterTrack>
<MeterIndicator />
</MeterTrack>
</Meter>
<ul className="space-y-1.5">
{steps.map(({ done, label }) => (
<li
className={`flex items-center gap-2 text-xs ${done ? "text-muted-foreground line-through" : ""}`}
key={label}
>
<span
className={`size-1.5 shrink-0 rounded-full ${done ? "bg-green-500" : "bg-muted-foreground/30"}`}
/>
{label}
</li>
))}
</ul>
</div>
);
}

