Billing Settings
NewSubscription settings for a SaaS dashboard: the current plan with usage meters that fill in, a monthly or yearly toggle with plans whose prices roll, the card on file, and a cancel flow with a confirm step.
motionlucide-reactutilsbadgebuttoncarditemprogressseparatorspinner@soultel/rolling-number@soultel/use-reduced-motion-safe
Installation
$ pnpm dlx shadcn@latest add @soultel/billing-settingsNo namespace set up? npx shadcn@latest add https://asafefe.dev/r/billing-settings.json works too.
Manual installation
- 1. Install the dependencies.
terminal1 lines npm install motion lucide-react - 2. Copy the source from the Code tab into components/billing-settings.tsx.
Usage
import { BillingSettings } from "@/components/billing-settings"
export function Billing({ account }: { account: Account }) {
return (
<BillingSettings
plans={[
{ id: "free", name: "Free", description: "For trying things out", price: { month: 0, year: 0 } },
{ id: "pro", name: "Pro", description: "For small teams", price: { month: 24, year: 230 } },
{ id: "team", name: "Team", description: "Unlimited projects and SSO", price: { month: 59, year: 566 } },
]}
defaultPlan={account.planId}
defaultInterval={account.interval}
renewsAt={account.renewsAt}
usage={[
{ id: "seats", label: "Seats", used: 4, limit: 5 },
{ id: "storage", label: "Storage", used: 18.4, limit: 50, unit: "GB" },
]}
paymentMethod={{ brand: "Visa", last4: "4242", expMonth: 8, expYear: 2028 }}
onUpdatePayment={() => openBillingPortal()}
onChangePlan={(planId, interval) => updateSubscription({ planId, interval })}
onCancel={(reason) => cancelSubscription({ reason })}
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| plans | BillingPlan[] | - | { id, name, description, price: { month, year } }[]. month is the monthly price, year the yearly total. 0 and 0 shows Free. |
| plan / defaultPlan | string | - | Controlled or initial id of the plan the account is on. |
| onPlanChange | (planId: string) => void | - | Called after a confirmed switch. |
| interval / defaultInterval | "month" | "year" | "month" | Controlled or initial billing interval. |
| onIntervalChange | (interval) => void | - | Called after a confirmed switch. |
| usage | UsageMeter[] | - | { id, label, used, limit, unit }[]. A meter turns vermilion at 90%. |
| paymentMethod | PaymentMethod | - | { brand, last4, expMonth, expYear } for the card on file. |
| onUpdatePayment | () => void | - | Shows an Update button by the card. |
| onChangePlan | (planId, interval) => void | Promise<void> | - | Called when the visitor confirms a switch. A promise shows a pending state; a rejection keeps the old plan. |
| onCancel | (reason?: string) => void | Promise<void> | - | Called from the confirm step with the picked reason. |
| onResume | () => void | Promise<void> | - | Called by Resume plan after a cancel. |
| cancelReasons | string[] | - | Chips offered in the cancel step. An empty array skips the question. |
| renewsAt | string | - | Next renewal as "YYYY-MM-DD". |
| defaultCancelled | boolean | false | Start in the cancelled state. |
| currency | string | "$" | Symbol shown before prices. |
