Staaarter

Billing Annual Discount

A billing promo card that encourages a subscriber to move from monthly to annual billing. It takes the plain monthly and annual prices and works out the discount percent, total savings, and months-free figure itself, then shows a monthly-equivalent price alongside a crossed-out monthly rate and a savings badge. Built for upgrade prompts inside a billing settings page or a pricing page's annual toggle callout.

By Staaarter Team
Billing
Updated February 18, 2026
Docs
Live preview

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-annual-discount.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/billing/billing19.tsx instead.

Usage

The file also exports billing19Demo, 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 { Billing19, billing19Demo } from "@/components/blocks/billing/billing19";

export default function Page() {
  return <Billing19 {...billing19Demo} />;
}

Props

PropTypeDefaultDescription
headingstring"Switch to annual billing"Heading at the top of the card.
descriptionstringnoneSupporting line under the heading.
planNamestringnoneOptional plan name shown next to the billed-annually line.
monthlyPricenumbernoneRegular monthly price in dollars. Used to compute the full annual price (times 12) and the savings.
annualPricenumbernoneDiscounted price for the whole year, in dollars. Divided by 12 for the displayed monthly-equivalent price.
featuresstring[]noneOptional bullet list of reasons to switch, rendered with check icons.
ctaLabelstring"Switch to annual"Label on the primary button.
ctaHrefstring"#"Link target for the primary button.
secondaryLabelstring"Stay monthly"Label on the secondary link, only rendered if secondaryHref is set.
secondaryHrefstringnoneLink target for the secondary action. Omit to hide the secondary button entirely.
classNamestringnoneExtra classes for the outer container.

Behavior notes

  • All pricing math happens inside the component from the two numeric props: full annual price is monthlyPrice times 12, savings is that figure minus annualPrice, and the discount badge percent and months-free estimate are derived from those, so you never pass a pre-computed savings string.
  • The savings badge only renders when the computed discount percent is greater than zero, so passing an annualPrice equal to or above monthlyPrice times 12 just hides the badge instead of showing a negative or zero discount.
  • The monthly price is shown struck through next to the new monthly-equivalent figure so the comparison reads at a glance without needing a separate toggle.
  • The secondary 'stay monthly' link only renders when secondaryHref is provided, so a single-CTA layout is just a matter of omitting that prop.

Frequently asked questions