Cost Breakdown
Spend by model, with token volume beside it. The two routinely disagree — the cheap model handling 90% of calls is often 10% of the bill — and either number alone leads to the wrong optimisation.
src/components/ui/cost-breakdown.tsxAbout
From the component's own source, where it was written.
Spend broken down by model, with token volume.
Cost and volume are shown side by side because they routinely disagree: the cheap model handling 90% of calls is often 10% of the bill, and a chart of either number alone leads to the wrong optimisation.
Precision follows magnitude rather than position: under a unit gets four decimals, anything larger gets two. A per-call cost rounded to cents is all zeroes, but an aggregate carried to four is both noise and the widest string the column ever has to hold.
That width matters because the currency prefix is locale-dependent — the default en-GB renders USD as "US$", so the same number is three characters wider than it is under en-US. The column is therefore a minimum width that can grow, never a fixed one that clips.
Install
npx astralyx-ui add cost-breakdown
Usage
import { CostBreakdown } from '@/components/ui/cost-breakdown'
<CostBreakdown rows={rows} period="August" budget={250} />Composer
Change the props and the source below follows.
- astralyx-large88%US$184.20
18,400 calls (6% of volume)12,400,000 in2,100,000 out
- astralyx-small10%US$21.60
214,000 calls (65% of volume)88,200,000 in9,600,000 out
- astralyx-embed2%US$4.10
96,200 calls (29% of volume)41,000,000 in
<CostBreakdown rows={rows} period="August 2026" budget={250} />Examples
Spend
<CostBreakdown rows={rows} period="August 2026" budget={250} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
CostBreakdown
Extends Omit<ComponentProps<'div'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | 'Spend' | — |
| rows | CostRow[] | required | { id, label, cost, inputTokens?, outputTokens?, calls?, color? }. Input and output are kept apart because they are priced differently. |
| currency | string | 'USD' | — |
| period | ReactNode | — | — |
| budget | number | — | Adds a budget line and flags an overage. |
| budgetLabel | ReactNode | 'Budget' | — |
| callsLabel | ReactNode | 'calls' | Follows the call count. |
| volumeLabel | (share: number) => ReactNode | DEFAULT_VOLUME_LABEL | That row's share of total calls, given the percentage. |
| inputTokensLabel | ReactNode | 'in' | Follows the input-token count. |
| outputTokensLabel | ReactNode | 'out' | — |
| width | min-w, not w | — | The currency prefix is locale-dependent — en-GB renders USD as "US$", three characters wider than en-US — so the amount column grows rather than clipping. |
Types
CostRow
Spend broken down by model, with token volume.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| label | ReactNode | — |
| cost | number | — |
| inputTokens? | number | Input and output tokens, kept apart — they are priced differently. |
| outputTokens? | number | — |
| calls? | number | — |
| color? | string | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| precision | by magnitude | Under a unit gets four decimals, larger amounts two. A per-call cost rounded to cents is all zeroes; an aggregate carried to four is noise, and the widest string the column has to hold. |