Billing Toggle
A monthly/yearly switch whose thumb slides and rolls the price digits, with the saving called out.
Overview
A two-option segmented control with a springy thumb. Switching period rolls each digit of the price like an odometer, only as far as it needs to go.
The saving is worked out from the two prices and shown on the Yearly option, and the note under the price says how it is billed.
When to use
- Pricing cards and checkout steps with a monthly and a yearly price.
- Any two-state choice where the number under it should visibly change.
When not to use
- More than two periods: use a segmented control with three options.
- Plans whose prices are not per month: the /mo suffix will mislead.
Installation
pnpm dlx shadcn@latest add https://sundaeui.dev/r/billing-toggle.jsonnpx shadcn@latest add https://sundaeui.dev/r/billing-toggle.jsonyarn dlx shadcn@latest add https://sundaeui.dev/r/billing-toggle.jsonbunx --bun shadcn@latest add https://sundaeui.dev/r/billing-toggle.jsonWorks in any React project. Files are written to components/sundae/. Read the source in the stage’s Code view. Pro assets need a personal token. See authenticated installs.
- No dependencies beyond React 18 or later.
- Copy billing-toggle.tsx and billing-toggle.css into components/sundae/.
- The CSS file is imported by the component, so no extra configuration is needed.
- Import the component and use it as shown below.
Usage
import { BillingToggle } from '@/components/sundae/billing-toggle'
<BillingToggle plan="Pro" monthly={24} yearly={18} onPeriodChange={setPeriod} />Props
| Prop | Type | Default |
|---|---|---|
monthly*Per-month price on monthly billing. | number | — |
yearly*Per-month price on yearly billing. | number | — |
planPlan name above the price. | string | 'Pro' |
currencySymbol before the price. | string | '$' |
period / defaultPeriodControlled or starting period. | 'monthly' | 'yearly' | 'yearly' |
onPeriodChangeCalled when the period changes. | (p: Period) => void | — |
fillSurface: dark or light. | 'dark' | 'light' | 'dark' |
Accessibility
- The switch is a radiogroup; arrow keys move between Monthly and Yearly with roving focus.
- The rolling digits are hidden from assistive tech; a visually hidden sentence ("$18 per month, billed yearly") is announced politely instead.
- Reduced motion removes the thumb spring and digit roll.
Customization
- --fg colours the thumb; the selected label takes --bg so it reads on it.
- Prices are numbers, so you can compute them from your plan data.
Keyboard
- Tab
- Focus the selected period
- ← / →
- Switch period
FAQ
- How is the saving worked out?
- From the two prices: 1 − yearly / monthly, rounded. If yearly is not cheaper the badge is hidden.
- Can I show prices with cents?
- Digits roll and any other character (like a decimal point) stays put, so 18.50 works.
- Is Billing Toggle free?
- Billing Toggle is part of Sundae Pro. The live preview is open to everyone; the source installs with a Pro account.
- How do I install Billing Toggle?
- Run npx shadcn@latest add https://sundaeui.dev/r/billing-toggle.json. The files are written to components/sundae/. It has no dependencies beyond React 18 or later.
- Does Billing Toggle support keyboard and screen readers?
- The switch is a radiogroup; arrow keys move between Monthly and Yearly with roving focus. The rolling digits are hidden from assistive tech; a visually hidden sentence ("$18 per month, billed yearly") is announced politely instead.
- Does Billing Toggle respect reduced motion?
- Reduced motion removes the thumb spring and digit roll.
More like this
All checkout →- Pro
Seat Pricing
Checkout · Refined - Pro
Plan Comparison
Checkout · Refined - Pro
Trial Countdown
Checkout · Refined - Pro
Seat Checkout
Checkout · Understated - Pro
Payment Methods
Checkout · Refined - Pro
Pause Subscription
Checkout · Refined