Billing Period Input

Reads a recurring price back in every billing period and catches a yearly price typed into the monthly field.

StopsYearly price typed into monthly plan

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/billing-period-input.json

The files land in your project. They're yours to edit.

Usage

billing-period-input-usage.tsx
import { BillingPeriodInput } from "@/components/ui/billing-period-input"

<BillingPeriodInput
  label="Pro plan price"
  defaultValue={{ amount: "5,988", period: "month" }}
  reference={{ amount: 499, period: "month", label: "the current Pro price" }}
/>

What it catches

  • A yearly price entered as monthly, or the reverse
  • A team total entered as a per-seat price
  • Large jumps from the reference price (current plan, plan below or last year's)
  • Misreading the rate, by showing it in every period

Props

BillingPeriodInput props
PropTypeDefault
label

Visible label.

string"Price"
value

Controlled value.

BillingPriceValue—
defaultValue

Initial value when uncontrolled.

Partial<BillingPriceValue>—
onValueChange

Called with the value and the parsed amount (null while it isn't a number).

(value: BillingPriceValue, amount: number | null) => void—
currency

ISO currency code.

string"INR"
locale

Locale for amounts, e.g. "en-IN" for ₹1,49,900 grouping.

string"en-IN"
periods

Periods offered, shortest first. All of them are read back.

BillingPeriod[]["week", "month", "quarter", "year"]
seats

Seats on the plan. When set, the price can be per seat or a total and the team total is read back.

number—
reference

A known price to compare against, which turns on the slip warnings.

PriceReference—
messages

Override any of the default messages.

Partial<typeof BILLING_PERIOD_MESSAGES>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label> tied to the input, described by an aria-live="polite" status
  • Price basis is a role="radiogroup" of role="radio" buttons
  • The period selector has an aria-label
  • Warnings use an icon plus text

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • The mix-up checks need a reference price; without one it only reads back
  • Weeks are converted as 12/52 of a month, so readbacks are approximate for weekly plans

What it stops

Yearly price typed into monthly plan

Also known as

pricing · plan · subscription · billing period · monthly yearly · per seat · dodo payments · stripe price

Field type

Dependencies

Pairs well with