Plan comparison
A React block (a whole screen) in Beamline's Blocks category.
Plans side by side against every feature: prices that roll between monthly and yearly, the current and recommended plan marked, a header that stays in view while the features scroll, differences only, and included, not included and not specified told apart.
Use it for
- An upgrade or pricing page where people compare plans feature by feature before choosing.
Not for
- Choosing a plan inside a checkout or settings form (radio-cards).
- Showing what the current plan uses (usage-meter).
Anatomy
- title and description
- billing period toggle (savings note)
- differences-only switch
- plan picker (narrow screens)
- sticky plan header (name, badges, price, period line, call to action)
- feature groups (group row, feature rows with an optional hint)
- cell: included (check) · not included (—) · a value · not specified (hatched)
Variants
- period: monthly, yearly
States
- monthly / yearly (prices roll)
- current plan (badge, its button says so)
- recommended plan (accent edge, badge, accent button)
- choosing (the plan's button busy, the others wait)
- choice failed (message under the button)
- differences only (equal rows hidden; nothing left says so)
- quote-only plan (Talk to sales)
- free plan
- narrow: one plan at a time with a picker; its header spans the row (name, price and button at full width)
- header stuck (hairline shadow)
Keyboard
- Tab: period toggle, differences switch, plan picker (narrow), each plan's button, then each feature hint
- arrow keys move within the period toggle and the plan picker
- hints open on focus and close with Escape
Motion
Prices roll digit by digit when the period changes (slot text); the stuck header gains its shadow over --motion-in. Reduced motion swaps prices at once.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
plans (required) | PlanComparisonPlan[] | — | id, name, description, price { monthly, yearly } per unit per month (null = quoted by sales, 0 = free), unit, cta. |
groups (required) | PlanFeatureGroup[] | — | Feature groups; each feature has values per plan id: true (included), false (not included), a string or number, or null (not specified). |
period / defaultPeriod / onPeriodChange | "monthly" | "yearly" | "yearly" | |
currentPlan | string | — | The plan the account is on. |
recommendedPlan | string | — | |
onSelectPlan | (planId, period) => void | Promise<void> | — | A plan's button; a promise shows progress on it and a rejection shows its message. |
differencesOnly / defaultDifferencesOnly / onDifferencesOnlyChange | boolean | — | |
format | (amount: number) => string | — | Price formatting. Default whole US dollars. |
title / description | ReactNode | — | |
unknownLabel | string | "Not specified" | |
stickyOffset | number | 0 | Pixels from the top of the scroller where the header sticks (below your own app bar). |
className | string | — | |
classNames | PlanComparisonClassNames | — | |
style / ref | CSSProperties / Ref<HTMLDivElement> | — |
Built from
- billing-toggle
- slot-text
- table
- button
- badge
- switch
- tooltip
- segmented-control
- empty-state
Dependencies
lucide-react, motion
Import
import { PlanComparison } from "@/blocks/plan-comparison/plan-comparison";
Get it
Plan comparison is part of Beamline: 190 React components and screens your coding agent (Claude Code, Codex, Cursor or any MCP client) installs into your app through Beamline's MCP server, as a ready-built package or as plain React source you can change. $49 one payment (regular $200), no subscription, a year of updates, one licence for your whole team. Get Beamline · Connect your agent
More in Blocks
- AI agent workspace: Threads, a conversation with streamed reasoning, cited answers and a plan that waits for approval then runs step by step, and an artifacts panel with…
- Analytics dashboard: A SaaS overview in the app shell, fed by your metrics hook: range and segment filters drive KPIs, new and expansion revenue, channels, plan mix, uptime…
- Auth page: The whole sign-in / sign-up route: product mark, a quiet dotted field with a light that follows the mouse, an optional notice, the SignIn and SignupForm…
- Changelog feed: An in-app What's new page: release notes grouped by month with tags, versions, pictures and details, a tag filter with counts, entries since your last…
- CRM pipeline: A pipeline screen: KPIs and stage mix derived from a virtualised deals grid with inline edit, bulk moves and delete with undo; a row opens the deal in a…
- Empty states: The five empty moments every product has, written and wired: first use with a first step, no results with the filters to clear, an error with retry and a…
- Page header: The top of a record or project page: breadcrumb, title with status, description and meta, a primary action with secondary actions that fold into a More…
- Settings: Profile and workspace settings with one draft and real save handler. Personal mode omits workspace data; optional profile fields share validation…
- Sign in: The sign-in card: email and password, a one-time code or a magic link, passkeys and single sign-on, with validation, progress, errors and a signed-in…
- Sign up form: The account creation card: single sign-on, name, work email and a password with strength and Caps Lock hints, terms consent, inline validation, your…
- Step form: The finished frame for any multi-step form you compose — onboarding, intake brief, questionnaire, booking, application: a wide card with progress, the…
- Trading terminal: A perpetuals terminal on your feed and your account: ticker tape, market header with funding, candles, book / trades / depth, order ticket, account…
- Voice agent: A voice agent in three layouts on one session: a support panel that mixes typing and talking, a call screen with the agent's orb and one call button, and…