Nasaq

UsageMeter

A quantity used against its limit (count, money or hours) with warning and danger thresholds, an unlimited state, budget burn with a pace projection, and a plan usage panel with an overage estimate strip.

PreviewOpen ↗

Code

import { UsageMeter } from "@fadymondy/nasaq/web";export function Seats() {  return (    <>      <UsageMeter label="Seats" used={46} limit={50} unit="seats" />      <UsageMeter label="Storage" used={12} limit={null} unit="GB" />      <UsageMeter label="AI spend" kind="money" currency="USD" used={182} limit={200} />    </>  );}

Data Display · beta

Live examples and controls: UsageMeter in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/usage-meter.json

How much of a limit is used. UsageMeter is one row: label, "used of limit", a bar that turns warning at 75% and danger at 90%, and a line saying so ("Approaching the limit", "Over the limit by 2,000"). It is built on the Nasaq Meter. BudgetBurn shows a project budget in hours and money with a tick for how much of the period has passed and a projected end figure. UsageSummary is the plan usage section: all resources, plus an estimated overage strip.

When to use

  • Seats, storage, API calls, AI spend, project hours against a plan or budget.
  • A plan usage or billing page that needs to show what the next invoice will add.

When not to use

  • Work that is running (an upload, an import): use Progress.
  • A single headline number with a trend: use StatCard.
  • Editing the limits themselves: use LimitsEditor.

Import

import { BudgetBurn, UsageMeter, UsageSummary } from "@fadymondy/nasaq/web";

Quick start

import { UsageMeter } from "@fadymondy/nasaq/web";

export function Seats() {
  return (
    <>
      <UsageMeter label="Seats" used={46} limit={50} unit="seats" />
      <UsageMeter label="Storage" used={12} limit={null} unit="GB" />
      <UsageMeter label="AI spend" kind="money" currency="USD" used={182} limit={200} />
    </>
  );
}

Anatomy

UsageMeter          data-slot="usage-meter"  data-tone="ok|warning|danger|over"  data-unlimited
  usage-meter-label / usage-meter-value
  Meter             data-slot="meter"        (or usage-meter-unlimited badge)
  usage-meter-marker  (when marker is set)
  usage-meter-status  (warning, danger, over)
BudgetBurn          data-slot="budget-burn"
UsageSummary        data-slot="usage-summary"
  usage-overage-strip  usage-overage-total

API

UsageMeter

PropTypeDefaultDescription
labelReactNoderequiredWhat is metered. Localise it.
ariaLabelstringlabel when a stringAccessible name when label is a node.
usednumberrequiredAmount used.
limitnumber | nullrequiredThe limit; null is unlimited (no bar, an Unlimited badge).
kind"count" | "money" | "hours""count"How amounts are formatted.
unitstringnoneNoun after a count ("seats", "GB").
currencystring"USD" ("SAR" in Arabic)ISO 4217 code for money.
thresholds{ warnAt?: number; dangerAt?: number }0.75 and 0.9Fractions of the limit for warning and danger.
markernumbernoneFraction of the period passed; draws a tick on the bar.
hintReactNoderemaining amountEnd-of-row text such as a projection.
size"sm" | "md""md"Bar thickness.
labelsUsageMeterLabelsen / arOverride any string.

BudgetBurn

hours?: { used, budget }, money?: { used, budget, currency? }, elapsed?: number (0 to 1), thresholds, labels. With elapsed, each bar projects the end of the period at the current rate.

UsageSummary

PropTypeDefaultDescription
planNameReactNoderequiredShown in the badge.
periodReactNode"Current period"Localised period text.
itemsUsageItem[]required{ id, label, used, limit, kind?, unit?, overageRate?, hint? }.
currencystring"USD" ("SAR" in Arabic)For the estimate and money items.
onUpgrade() => voidnoneShows Upgrade plan when any item is near or over its limit.
loadingbooleanfalseSkeleton layout.

The overage estimate is the sum over items of max(0, used - limit) * overageRate. The pure helpers usageTone, usageFraction, overageAmount, overageTotal and burnProjection are exported too.

Examples

Budget burn:

import { BudgetBurn } from "@fadymondy/nasaq/web";

export const Burn = () => <BudgetBurn hours={{ used: 96, budget: 160 }} money={{ used: 7200, budget: 12000, currency: "USD" }} elapsed={0.5} />;

Plan page:

import { UsageSummary } from "@fadymondy/nasaq/web";

export const Page = () => (
  <UsageSummary
    planName="Team"
    currency="USD"
    items={[
      { id: "seats", label: "Seats", used: 46, limit: 50, unit: "seats" },
      { id: "calls", label: "API calls", used: 1_200_000, limit: 1_000_000, overageRate: 0.00001 },
    ]}
  />
);

Arabic: wrap the app in NasaqProvider locale="ar"; the strings switch and the bar fills from the right.

Accessibility

The bar is a Base UI meter with an accessible name and a spoken value ("46 of 50"). The state is written out with an icon and a sentence, so it never depends on colour. Over the limit the sentence has role="alert". The projection and the overage strip are plain text.

RTL & i18n

  • Layout uses logical properties; the fill and the period tick start at the inline start.
  • Amounts are formatted with the active locale and isolated with <bdi> so "1,200 seats" keeps its order in Arabic.
  • Every string has an English and Arabic default; override with labels.

Styling & tokens

  • Fill colours come from --primary, --nq-warning, --nq-danger through Meter.
  • Target [data-slot="usage-meter"][data-tone="danger"] for state styling.

Do / Don't

  • Do give an overageRate only when the plan really charges past the limit.
  • Do pass elapsed to a budget so a fast burn is visible before the limit is hit.
  • Don't hide unlimited resources; the Unlimited badge tells the reader nothing will run out.
  • Don't rely on the bar colour alone to say a limit is close.

Lab

https://docs.nasaqui.com/?path=/docs/components-data-display-usage-meter--docs

On this page