Pricing Table React component
Pricing plans side by side with a monthly / yearly switch, prices that roll when the period changes, a highlighted plan with a badge, included and excluded features, and a custom-price plan for enterprise.
About the Pricing Table component
Pricing Table shows your plans side by side, with a Monthly / Yearly switch on top. The selected period slides between the two options, and every price rolls up or down to its new value when the period changes. Under each price a short line says Free forever, Billed monthly or the yearly total, for example $228 billed yearly.
Each plan has a name, a description, the price, a full-width button and a feature list. Features marked included: false are crossed out with an x. The highlighted plan is raised a little, gets a brighter border, a soft shadow, a primary button and a Most popular badge. A plan with price null shows Custom and a Contact sales button. Cards fade up one after another on load, and on phones they stack in one column.
When to use it
Use it on the pricing page or the pricing section of a SaaS, app or membership site, anywhere people compare plans before they pay.
Pass href to send a plan to a checkout or contact page, or handle onSelect to open your own checkout with the plan id and billing period.
Installation
Add the Pricing Table component to your project with the shadcn CLI. It lands in components/pricing-table.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/pricing-tableOr copy the source by hand and install its dependencies:
npm install motion clsx tailwind-mergeProps
<PricingTable /> accepts 11 props, all optional.
| Prop | Type | Default |
|---|---|---|
| plans | PricingPlan[] | DEFAULT_PLANS |
| billing | "monthly" | "yearly" | — |
| defaultBilling | "monthly" | "yearly" | "yearly" |
| onBillingChange | (billing: Billing) => void | — |
| onSelect Called when a plan's button is pressed (plans without href). | (planId: string, billing: Billing) => void | — |
| showBillingToggle Hide the monthly / yearly switch, for plans that only have one price. | boolean | true |
| yearlyNote Pill next to "Yearly". Pass "" to hide it. | string | "Save 20%" |
| currency | string | "$" |
| period Text after the price, e.g. "/month" or "/seat". | string | "/month" |
| theme | "dark" | "light" | "dark" |
| className | string | — |
- plansPricingPlan[]Default: DEFAULT_PLANS
- billing"monthly" | "yearly"
- defaultBilling"monthly" | "yearly"Default: "yearly"
- onBillingChange(billing: Billing) => void
- onSelect
Called when a plan's button is pressed (plans without href).
(planId: string, billing: Billing) => void - showBillingToggle
Hide the monthly / yearly switch, for plans that only have one price.
booleanDefault: true - yearlyNote
Pill next to "Yearly". Pass "" to hide it.
stringDefault: "Save 20%" - currencystringDefault: "$"
- period
Text after the price, e.g. "/month" or "/seat".
stringDefault: "/month" - theme"dark" | "light"Default: "dark"
- classNamestring
npm dependencies
Installed for you by the shadcn CLI.
Pricing Table FAQ
- How do I set monthly and yearly prices?
- Give price as { monthly, yearly }, both per-month amounts. The switch shows one or the other, and in yearly mode the line under the price shows the yearly total (yearly x 12). A plain number is the same price for both, and null shows Custom.
- How do I connect the buttons to checkout?
- Either give a plan an href (Stripe Payment Links, Lemon Squeezy or your own page) and the button becomes a link, or leave href out and handle onSelect(planId, billing) to start checkout in code.
- Can I hide the Monthly / Yearly switch?
- Yes, set showBillingToggle={false} when your plans have only one price. You can also control the period yourself with billing and onBillingChange, for example to keep it in the URL.
- How many plans can it show?
- Any number. Two plans sit in two columns, three in three, and four or more in two columns on tablets and four on large screens. On phones the cards always stack.
- Can I change the currency or the period text?
- Yes. currency (default $) goes before the number, period (default /month) after it, and yearlyNote sets the pill next to Yearly (pass an empty string to hide it).