A workspace's billing at a glance: the next invoice, usage meters that turn warning at 80%, and a cancel flow with a reason step. Built on Dialog, Radio Group, Auto Height and Number Roll.
When to use
- The Billing tab of a SaaS settings page.
- When usage limits matter: events, seats, storage.
- When cancelling should ask why, then confirm, without leaving the page.
When not to use
- Comparing plans to buy: use a pricing table or Usage-based Pricing Calculator.
- A list of past invoices: use a Table.
Installation
npx shadcn add @eased-pro/billing-overviewNeeds the @eased-pro registry and your Pro token in components.json. Create a token and copy the setup from your account .
Usage
import { BillingOverview } from "@/components/eased/billing-overview/billing-overview";<BillingOverview plan={{ name: "Growth", interval: "month", renewsAt: Date.UTC(2026, 9, 31) }} lines={[{ id: "plan", label: "Growth plan", amount: 99 }, { id: "sync", label: "Warehouse sync", amount: 30 }]} usage={[ { id: "events", label: "Events", used: 1_642_000, limit: 2_000_000, overage: "Past 2M, events cost $0.09 per 1K." }, { id: "seats", label: "Seats", used: 7, limit: 10 }, ]} card={{ brand: "Visa", last4: "4417" }} cancelReasons={[{ id: "price", label: "It costs too much" }, { id: "other", label: "Something else" }]} onCancel={(reason) => api.cancel(reason)} actions={<Button size="sm">Change plan</Button>}/>API reference
BillingOverview
| Prop | Type | Default | Description |
|---|---|---|---|
plan | { name, interval, renewsAt } | ||
lines | { id, label, amount }[] | The upcoming invoice; its total is the focal number and the lines are listed under it. | |
maxLines | number | 3 | Past this, the first maxLines - 1 lines show and the rest fold behind an "N more" disclosure. |
usage | BillingUsage[] | [] | { id, label, used, limit, format?, overage? }. A meter turns warning at 80%; overage explains the cost. |
card | { brand, last4, expires? } | Card on file. | |
cancelReasons | { id, label }[] | [] | The cancel flow's first step. No reasons, no Cancel button. |
canceled / defaultCanceled | boolean | false | The plan ends at renewsAt. |
onCancel / onResume | (reason) => void / () => void | ||
actions | ReactNode | Leading footer slot. | |
currency / locale | string | "USD" / "en-US" |
UsageMeter (from _primitives/saas-core) is the meter on its own.
Keyboard
| Key | Action |
|---|---|
Enter on Cancel plan | Opens the dialog; focus moves to the first reason. |
↑ / ↓ | Choose a reason. |
Escape | Closes the dialog and returns focus to Cancel plan. |
Enter / Space on N more | Shows or hides the folded invoice lines. |
Accessibility
- Meters use
role="meter"with a spoken value ("1.64M of 2M, 82%"). - The dialog is Base UI's: focus is trapped, labelled by its title and described by its description.
- Continue stays disabled until a reason is chosen; the final step names the consequence before the destructive button.
Motion
The invoice rolls on the snappy spring; meter fills slide in on smooth and recolour at 80%. In the dialog the body cross-fades between steps with a blur while its height springs (Auto Height), and the primary button morphs from "Continue" to "Cancel plan" rather than swapping. The "N more" lines unfold on Auto Height. Cancelling rolls the invoice and its lines to zero, folds the lines away and unfolds a Resume banner in their place. Reduced motion: values change at once and steps fade.
Notes for AI
Use it on a billing settings page. Pass the upcoming invoice as lines so the total is derived, and give every usage meter that can cost money an overage sentence.
Don't build a separate confirm dialog: the two-step flow is inside. Don't colour meters red at their limit; a limit is information (warning), not an error.
Related
- Usage-based Pricing Calculator: estimating before buying.
- Dialog: the cancel flow's surface.