build69
build69  /  Components  /  Marketing Sections  /  Pricing

Pricing

v1.0.18Updated 19 Oct 2026

Pricing section with tier cards, billing toggles and comparison tables. Copy-paste block; baked-in demo content (edit the JSX to customise).

Preview

Live · 13 stories
Three TierOpen ↗
▸Why this is recommendedpricingplanstiersbilling+4

Pricing — three tier.

Recommended for+14
  • ✓Landing pages
  • ✓Pricing pages
  • ✓Marketing pages
Tags+6
pricingplanstiersbillingmarketingsectionthreetier
Features+4
  • Three-tier, monthly/annual toggle, comparison-table, single-plan and feature-list layouts
  • Highlighted "popular" tier
  • i18n: prices and counts format through the locale layer (LocaleProvider / useB69Locale) — the ฿/THB demo currency stays baked per the section-block contract
  • Configured entirely by props, never by editing the vendored file: `plans` is the single source of truth (name, blurb, monthly, yearly, features, cta, ctaHref, popular) and drives every layout
  • The comparison table DERIVES its column names, prices and popular column from `plans`; `compareGroups` carries only feature rows, index-aligned, and a short row renders the missing cells as not-included. Up to 1.0.13 COMPARE_PRICES kept a second copy of the tier prices, so the table and the cards could silently disagree (condo69 fb_bc8045e2 / fb_f8fba1de)
  • Prices have NO default: with nothing passed a block renders its heading, eyebrow and subcopy plus a short empty note from labels (emptyPlans / emptyCompare / emptyAddons / emptyUsage) - never an empty grid, an empty table body, a baht-undefined or a NaN
  • No dead controls: a plan button renders ONLY when the plan has a ctaHref or the family has an onPlanSelect (destination to anchor, handler to real type=button, neither to nothing). Each block CTA follows the same rule through ctaHref / onCtaClick
  • Client-side routing: pass `linkComponent` (next/link, React Router Link) and every anchor renders through the host app link component instead of reloading the document (fb_bf7dc63a)
  • currencySymbol replaces the baked baht sign everywhere, animated totals included, so a consumer outside Thailand is not stuck billing in baht; digit grouping still follows LocaleProvider
Not recommended for−2
  • ✕In-app billing settings

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "Pricing" })

Usage

import { PricingThreeTier } from '@/components/sections/pricing';

<PricingThreeTier />

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
className

Optional wrapper class override

string—
labels

Override every built-in display string across the pricing family (eyebrows, headings, subcopy, billing-period toggle, per-plan chrome, button text, footnotes, aria-labels) for i18n or white-labelling. Merged over DEFAULT_PRICING_LABELS.

Partial<PricingLabels>—
plans

The plans on sale: [{ id?, name, blurb?, monthly, yearly?, features?, cta?, ctaHref?, popular? }]. Drives every layout; the single-plan blocks price plans[0]. Defaults to [] - a price is a commercial fact nobody proofreads, so a vendored copy shows labels.emptyPlans until you pass yours (fb_bc8045e2).

PricingPlan[]—
compareGroups

PricingComparisonTable feature rows: [{ group, rows: [{ label, values }] }], each row index-aligned with `plans`. Column headers, prices and the popular column come from `plans`, never from here. Defaults to [].

PricingCompareGroup[]—
addons

PricingWithToggleAndAddons modules: [{ id?, name, blurb?, monthly, yearly? }], priced on top of plans[0]. Defaults to [] and the list is replaced by labels.emptyAddons.

PricingAddon[]—
featureGroups

PricingWithFeatureList checklist: [{ title, items }]. Defaults to [] and the list is omitted entirely.

PricingFeatureGroup[]—
enterpriseFeatures

PricingEnterpriseSplit bullets inside the contact-sales panel. Defaults to [].

string[]—
perSeatPrice

PricingUsageBased: what one seat costs per month. No default - without it (or invoiceBlockPrice) the block shows labels.emptyUsage instead of a calculator that quotes a price nobody set.

number—
invoiceBlockPrice

PricingUsageBased: what one block of invoices costs per month. No default, same reason as perSeatPrice.

number—
invoiceBlockSize

PricingUsageBased: how many invoices one block buys. Defaults to 500; the matching copy spells the number out, so override labels.usageInvoicesPrice / usageInvoicesSlider with it.

number—
currencySymbol

The symbol in front of every amount, the animated totals included. Defaults to the baht sign (byte-identical to what these blocks shipped); one prop, so a card and a total cannot disagree.

string—
onPlanSelect

What a plan button does. On its own it makes the button a real type=button; with plan.ctaHref it fires before navigation. With neither, no plan button renders.

(plan: PricingPlan) => void—
ctaHref

Where the block own CTA goes (add-on summary, usage estimate, enterprise contact sales). Unwired, it renders nothing.

string—
onCtaClick

What the block own CTA does; receives the click event so a routed host can preventDefault (fb_bf7dc63a).

(event: React.MouseEvent<HTMLElement>) => void—
linkComponent

The host link component (next/link, React Router Link) for every anchor this family renders. Defaults to a plain anchor, which in a routed app means a full document reload (fb_bf7dc63a).

PricingLinkComponent—

Related