Billing Yearly Savings Toggle
A compact pricing toggle for a single plan. Switching between monthly and yearly recalculates the displayed price to the effective monthly rate, and a savings line shows the total amount and percentage saved by choosing annual billing. A 'Best value' badge appears next to the plan name whenever yearly is active and there's a real discount to show.
Docs
Installation
A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its badge, button dependencies and any missing npm packages, and installs them straight into your project.
npx shadcn add https://staaarter.com/r/billing-yearly-savings-toggle.jsonPrefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/billing/billing150.tsx instead.
Usage
The file also exports billing150Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.
import { Billing150, billing150Demo } from "@/components/blocks/billing/billing150"; export default function Page() { return <Billing150 {...billing150Demo} />; }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| heading | string | none | Optional heading above the toggle. |
| description | string | none | Optional supporting text under the heading. |
| planName | string | "Pro" | Plan name shown above the price. |
| monthlyPrice | number | none | Price per month when billed monthly. |
| annualPrice | number | none | Total price for the year when billed annually, not a monthly figure. |
| currencySymbol | string | "$" | Symbol used in every formatted amount. |
| defaultYearly | boolean | true | Whether the yearly option is active on first render. |
| ctaLabel | string | "Continue" | Label on the bottom button. |
| ctaHref | string | "#" | Destination for the bottom button. |
| className | string | none | Extra classes for the outer container. |
Behavior notes
- isYearly is local state; toggling it swaps the displayed price between monthlyPrice and annualPrice divided by 12, formatted to the nearest cent.
- Total savings is computed as (monthlyPrice * 12) minus annualPrice, and the percentage is that savings divided by the monthly total, rounded to the nearest whole percent.
- The savings line and the 'Best value' badge are both skipped when the computed savings is zero or negative, so passing an annualPrice that isn't actually cheaper hides them rather than showing a misleading number.
- The savings line stays in the layout at all times to avoid a height jump when toggling; it's just faded to opacity-0 while monthly is active.
- The bottom button is a next/link styled as a button, not a form submit, so wiring it to checkout or an upgrade flow is up to the caller.
Frequently asked questions
More Billing Blocks
View all →Billing Invoice List
Invoice history table with status badges, formatted amounts, and a per-row download link, built with shadcn/ui and Tailwind CSS.
Billing Failed Payment Alert
High-contrast failed-payment alert with a message, a retry button, and an optional update-payment-method button, built with shadcn/ui and Tailwind CSS.
Billing Credit Balance
Compact credit balance card with a large total, an optional purchased-versus-promotional breakdown, a low-balance notice, and a top-up button.
Billing Payment Methods
A saved payment method list for React and Next.js with brand icons, a default badge, near-expiry warnings, and remove/set-default actions per row.
Billing Add Card
A card entry form for React and Next.js with auto-formatted fields, internal brand detection, and per-field validation feedback built with shadcn/ui and Tailwind CSS.
Billing Coupon Code
An inline promo code input for React and Next.js with a simulated loading state, an applied summary showing the discount amount, and an invalid-code message.