Context window41.2K / 200K(21%)
Requests per minute860 / 1K(86%)
Monthly budget$97.40 / $100(97%)
import { UsageMeter, UsageMeterGroup } from "@/components/pandacoderz-ui/usage-meter";
export default function UsageMeterDemo() {
return (
<div className="w-full max-w-sm">
<UsageMeterGroup>
<UsageMeter label="Context window" value={41_200} max={200_000} showPercent />
<UsageMeter label="Requests per minute" value={860} max={1_000} showPercent />
<UsageMeter label="Monthly budget" value={97.4} max={100} format={(v, m) => `$${v.toFixed(2)} / $${m}`} showPercent />
<UsageMeter value={30} max={100} size="sm" />
</UsageMeterGroup>
</div>
);
}Installation
npx shadcn@latest add https://ui.spencerwueste.com/r/usage-meter.jsonpnpm dlx shadcn@latest add https://ui.spencerwueste.com/r/usage-meter.jsonyarn dlx shadcn@latest add https://ui.spencerwueste.com/r/usage-meter.jsonbunx --bun shadcn@latest add https://ui.spencerwueste.com/r/usage-meter.jsonCopy the source into your project:
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
type UsageMeterProps = Omit<React.ComponentProps<"div">, "children"> & {
value: number;
max: number;
label?: React.ReactNode;
/** Format the value / max pair. Defaults to compact numbers. */
format?: (value: number, max: number) => string;
/** Percent thresholds that switch to warning / danger colours. */
warnAt?: number;
dangerAt?: number;
size?: "sm" | "md";
showPercent?: boolean;
};
const compact = new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 });
function defaultFormat(value: number, max: number) {
return `${compact.format(value)} / ${compact.format(max)}`;
}
function UsageMeter({
value,
max,
label,
format = defaultFormat,
warnAt = 80,
dangerAt = 95,
size = "md",
showPercent = false,
className,
...props
}: UsageMeterProps) {
const pct = max > 0 ? Math.min(100, Math.max(0, (value / max) * 100)) : 0;
const tone = pct >= dangerAt ? "danger" : pct >= warnAt ? "warn" : "ok";
return (
<div
data-slot="usage-meter"
data-tone={tone}
role="meter"
aria-valuemin={0}
aria-valuemax={max}
aria-valuenow={value}
className={cn("flex w-full flex-col gap-1.5", className)}
{...props}
>
{label !== undefined || showPercent ? (
<div className="flex items-center justify-between gap-3 text-xs">
<span className="truncate text-muted-foreground">{label}</span>
<span className="shrink-0 font-mono tabular-nums text-foreground">
{format(value, max)}
{showPercent ? <span className="ml-1.5 text-muted-foreground">({Math.round(pct)}%)</span> : null}
</span>
</div>
) : null}
<div className={cn("w-full overflow-hidden rounded-full bg-muted", size === "sm" ? "h-1" : "h-1.5")}>
<div
className={cn(
"h-full rounded-full transition-[width] duration-500 ease-out",
tone === "ok" && "bg-brand",
tone === "warn" && "bg-amber-500",
tone === "danger" && "bg-red-500",
)}
style={{ width: `${pct}%` }}
/>
</div>
</div>
);
}
function UsageMeterGroup({ className, ...props }: React.ComponentProps<"div">) {
return <div data-slot="usage-meter-group" className={cn("flex flex-col gap-4", className)} {...props} />;
}
export { UsageMeter, UsageMeterGroup };Usage
import { UsageMeter, UsageMeterGroup } from "@/components/pandacoderz-ui/usage-meter";<UsageMeter label="Context window" value={41_200} max={200_000} showPercent />
<UsageMeter label="Budget" value={97.4} max={100} format={(v, m) => `$${v} / $${m}`} />The bar turns amber at warnAt percent and red at dangerAt percent.
API Reference
| Prop | Type | Description |
|---|---|---|
value / max |
number |
Current and maximum. |
label |
ReactNode |
Left-hand label. |
format |
(value, max) => string |
Right-hand text. Defaults to compact numbers. |
showPercent |
boolean |
Appends a percentage. |
warnAt / dangerAt |
number |
Thresholds in percent. Defaults 80 and 95. |
size |
"sm" | "md" |
Bar thickness. |