Staaarter

Billing Custom Billing Cycle

A billing cycle block that lets someone pick between monthly, quarterly, annual, or any custom cycle you define. Selecting a cycle recalculates the effective monthly price and the total charged per cycle, and shows a savings badge on any cycle with a discount attached. Built with TypeScript, Tailwind CSS, and Lucide icons.

By Staaarter Team
Billing
Updated April 29, 2026
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its badge dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/billing-custom-billing-cycle.json

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

Usage

The file also exports billing44Demo, 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 { Billing44, billing44Demo } from "@/components/blocks/billing/billing44";

export default function Page() {
  return <Billing44 {...billing44Demo} />;
}

Props

PropTypeDefaultDescription
headingstring"Billing cycle"Block heading.
descriptionstringnoneSupporting text under the heading.
basePricenumbernoneFull monthly price before any cycle discount is applied.
cyclesBillingCycleOption[]noneCycle options rendered as toggle buttons, in the order given.
defaultCycleIdstringcycles[0].idWhich cycle is selected on first render.
notestring"Cycle changes take effect at the end of your current period. Prorated adjustments may apply."Small info line under the price, pass an empty string to hide it.
classNamestringnoneExtra classes for the outer container.

Types

interface BillingCycleOption {
  id: string;
  label: string;
  months: number;
  discountPercent?: number;
}

Behavior notes

  • The selected cycle is local state, defaulting to defaultCycleId or the first entry in cycles.
  • The displayed monthly price is basePrice reduced by the selected cycle's discountPercent, not a value read directly off the cycle object, so the price and the badge always agree.
  • The per-cycle total shown under the price is the effective monthly price multiplied by that cycle's months.
  • A cycle only shows a 'Save X%' badge when its discountPercent is set and greater than zero, cycles without a discount render as a plain label.
  • The info note at the bottom is static text meant to set expectations about proration, it isn't computed from the actual switch and doesn't know the current invoice.

Frequently asked questions