Early bird: 30% off Dashral ProSee pricing

Dashral

AI usage & limitsPro

What your product's AI costs this month: the spend against its budget and where it's heading, every day by model, and each API key against its limit. Edit the budget and its line glides to show what it means; a key at its limit pauses, an alert rises as the spend passes its mark. Live, on motion.

Installation

When to use

  • The page where a team sees what its AI costs this month: the spend against a budget, where it's heading, by model and by API key.
  • AI products that bill by usage and need limits people can set: a monthly budget that alerts or pauses, and a limit per key.

When not to use

  • One run's cost: Agent run viewer shows the tokens and cost of each step.
  • A customer's plan, card and invoices: Billing settings.

Usage

AI usage & limits with your own data: every prop has the sample as its default, so pass only what you have.

example.tsx
"use client"

import { useRef, useState } from "react"
import { AiUsage, type AiUsageData, type AiUsageHandle } from "@/components/dashral/blocks/ai-usage"

export default function Usage() {
  const usage = useRef<AiUsageHandle>(null)
  // Your month's usage, kept in state: a new object replaces what's shown.
  const [month] = useState<AiUsageData>({
    period: { label: "November", start: "2026-11-01", length: 30 },
    models: [
      { id: "large", name: "Large model" },
      { id: "small", name: "Small model" },
    ],
    // One entry a day from the period's start, by model id; the last is today.
    days: [
      { large: { cost: 42.1, tokens: 7_000_000, requests: 2200 }, small: { cost: 8.4, tokens: 14_000_000, requests: 9800 } },
      { large: { cost: 51.6, tokens: 8_600_000, requests: 2700 }, small: { cost: 9.9, tokens: 16_500_000, requests: 11200 } },
    ],
    keys: [
      { id: "key_1", name: "Support bot", owner: "Support", spent: 61.2, limit: 400 },
      { id: "key_2", name: "Website chat", owner: "Marketing", spent: 50.8, limit: null },
    ],
  })
  // A request made while the page is open: usage.current?.record({ model: "small", key: "key_1", cost: 0.02, tokens: 3400 })
  return (
    <AiUsage
      ref={usage}
      usage={month}
      limits={{ budget: 2000, onReached: "pause", alerts: [50, 80, 100] }}
      live
      // While the promise is pending Save shows it; a thrown Error's message shows beside it.
      onLimitsChange={async (limits) => console.log("limits", limits)}
      onKeyLimitChange={async (key, limit) => console.log(key.id, "limit", limit)}
      onAlert={(percent, spent) => console.log(`${percent}% of the budget used: ${spent}`)}
    />
  )
}

API reference

AiUsage

What a product's AI costs this period: the spend against the budget and where it's heading, every day by model (cost, tokens or requests), each model's share, and each API key against its limit. Edit the budget, what happens when it's reached and the alerts, and the bar shows what they'd mean before you save. Requests made while the page is open come in through the handle's record.

PropTypeDefault
titleReact.ReactNode"AI usage"
usageAiUsageData

The period's usage. A new object replaces what's shown (and what record added), so keep yours in state.

sample
limitsUsageLimits

The limits at first: saving here changes them.

sample
currencystring

ISO 4217 code for every amount.

"USD"
onLimitsChange(limits: UsageLimits) => Promise<void> | void

Save the budget, what happens when it's reached and the alerts. Save shows it's working while the promise is pending; a thrown Error's message shows beside it.

—
onKeyLimitChange(key: UsageKey, limit: number | null) => Promise<void> | void

Set a key's limit (null for none). Same as onLimitsChange: working while pending, a thrown Error's message shows.

—
onAlert(percent: number, spent: number) => void

The spend passed one of the alerts' marks while the page is open, by record.

—
liveboolean

Shows "Live" in the header, breathing: say so while record is fed.

false
refReact.Ref<AiUsageHandle>—
classNamestring—

UsageBudgetMeter

The spend on a bar against the budget: what's spent, where the period ends at this pace (striped, warning where it passes the budget), the budget as a line across it and the alerts' marks under it. Everything glides to new values, the budget's line too, so a budget being edited shows what it would mean.

PropTypeDefault
spentnumber

Required.

—
forecastnumber

Required. Where the period ends at this pace: drawn on from the spend, striped, and in warning past the budget.

—
budgetnumber

Required.

—
alertsnumber[]

Percentages of the budget marked under the bar; the ones passed fill in.

[]
formatIntl.NumberFormat

Required.

—

UsageBurnUp

The spend adding up over the period against the budget's line: drawn in when it first shows, then going on dashed at this pace to the period's end, with a mark where that crosses the budget. Lines glide when the spend or the budget changes.

PropTypeDefault
daysnumber[]

Required. What each day cost, from the period's start; the last is today.

—
periodUsagePeriod

Required.

—
budgetnumber

Required.

—
heightnumber112
classNamestring—

UsageChart

Every day of the period as a bar, its models stacked in it; the days still to come drawn dashed at the average so far. Hover, or focus it and use the arrow keys, to read a day: the others fade back. Bars grow up from the axis when it first shows and glide to their new heights when the metric or the data changes.

PropTypeDefault
modelsUsageModel[]

Required.

—
daysRecord<string, UsageAmount>[]

Required. One entry a day from the period's start; the last one is today.

—
periodUsagePeriod

Required.

—
metricUsageMetric

Required.

—
focusstring | null

A model to pick out: the others fade back.

null
daynumber | null

The day being read (from 0), or null.

null
onDayChange(day: number | null) => void—
formatIntl.NumberFormat

Required.

—
heightnumber232

type UsageMetric

= "cost" | "tokens" | "requests"

type UsageAmount

What a model used in a day: what it cost (in the block's currency), its tokens, its requests.

PropTypeDefault
costnumber—
tokensnumber—
requestsnumber—

type UsageModel

color is any CSS color; left out, the chart's palette in order (--chart-1 to --chart-5). icon is the model's or its provider's logo (an SVG, an <img>), shown in its row in place of the color swatch; the share bar keeps the color.

PropTypeDefault
idstring—
namestring—
colorstring—
iconReact.ReactNode—

type UsageKey

An API key, or anything else you bill against: an agent, a team. A key at its limit is paused.

PropTypeDefault
idstring—
namestring—
ownerstring—
spentnumber—
limitnumber | null—

type UsagePeriod

The billing period: its name ("November"), its first day ("2026-11-01") and how many days it has.

PropTypeDefault
labelstring—
startstring—
lengthnumber—

type AiUsageData

PropTypeDefault
periodUsagePeriod—
modelsUsageModel[]—
daysRecord<string, UsageAmount>[]

One entry a day from the period's start, by model id; the last one is today.

—
keysUsageKey[]—

type UsageLimits

The month's budget, what happens when it's reached ("alert", or "pause" every request until the next period), and the percentages of it that raise an alert.

PropTypeDefault
budgetnumber—
onReached"alert" | "pause"—
alertsnumber[]—

type UsageEvent

One request, or a batch of them, as it's made: today's numbers, its model's and its key's grow by it.

PropTypeDefault
modelstring—
keystring—
costnumber—
tokensnumber—
requestsnumber—

type AiUsageHandle

PropTypeDefault
record(event: UsageEvent) => void

Adds a request to today: the numbers turn, today's bar grows, and the alerts and key limits it passes go off. Left out while it's paused (the budget reached with "pause", or its key at its limit): a paused request never ran.

—