Pricing
ProA responsive marketing pricing table. Named parts you assemble into plan cards: a name, a big tabular price, a feature checklist and a bottom-pinned call to action. Mark the recommended plan featured.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
Pricing,
PricingTierBanner,
PricingTier,
PricingTierHeader,
PricingTierName,
PricingTierDescription,
PricingPrice,
PricingAmount,
PricingPeriod,
PricingFeatures,
PricingFeature,
PricingFeatureGroup,
PricingTierAction,
} from "@/components/ui/pricing"
<Pricing>
<PricingTierBanner label="…">
<PricingTier>
<PricingTierHeader>
<PricingTierName />
<PricingTierDescription />
</PricingTierHeader>
<PricingPrice>
<PricingAmount />
<PricingPeriod />
</PricingPrice>
<PricingFeatures>
<PricingFeature />
</PricingFeatures>
<PricingFeatureGroup label="…">
<PricingFeature icon={…} />
</PricingFeatureGroup>
<PricingTierAction>…</PricingTierAction>
</PricingTier>
</PricingTierBanner>
</Pricing>Card styles#
The tone prop on PricingTier sets the card's surface, and is orthogonal to featured (the recommended ring), so the two combine. default is the bare card (a transparent fill, so it blends with whatever it sits on, delineated by its border alone); outline swaps the hairline for an ink edge; solid fills the card with the accent and flips every part to inverse text and glyphs (pair it with a light CTA and a light badge); muted sits the card on a soft grey panel; dashed draws the bare card on a dashed contour, for an open-ended plan (a custom quote, a service) beside fixed-price tiers. The CTA's Button variant stays your choice.
Banner#
Wrap a tier in PricingTierBanner for the banner-behind treatment: the card nests inside a brand panel that peeks as a top label and a thin accent edge all around. It composes over any tone and, inside a grid, lines up with un-bannered neighbours.
Action position#
actionPosition sets where the CTA sits. bottom (default) pins it to the card foot via mt-auto, so CTAs line up across cards no matter how long each list is. top lets it sit in flow right under the price, with the checklist below: place PricingTierAction accordingly in the markup.
Feature states#
Every PricingFeature is one of three states. Included rows (the default) get a filled success check. Mark a row included={false} for a filled cross and a dimmed label, so a single list reads as have vs. have-not. Add a hint to either to append an info glyph whose tooltip carries the caveat or limit, keeping the label terse.
Features with icons#
For a lifetime or all-in bundle, the value isn't a have vs. have-not checklist, it's a long, categorised inventory. Swap the flat PricingFeatures for stacked PricingFeatureGroup sections: each is a centered Divider header (with an optional icon) over its own list, and every PricingFeature carries a contextual icon that reads neutral (the success-check tint stays reserved for the default checklist). The rows sit at full text strength because the icon, not a dimmed weight, marks the line, and any row can wrap a link. Here the CTA floats under the price with actionPosition="top" so the inventory reads beneath it.
Size#
size sets a tier's type scale. md (default) is the compact card for a docs page, a dialog or a three-up row. lg is the landing-page scale, where a long categorised inventory is read at body size: 16px rows, a 48px price, a 20px name and a readable group label. The name, price, rows and group labels step together, so the card keeps its hierarchy at either size.
Billing cycle#
The component is stateless: you own the prices, so a billing toggle is a few lines of state that swap the PricingAmount and PricingPeriod. Because the amount is tabular-nums, the digits change without the row reflowing. Here a Tabs control drives a columns={2} table, and a small RollingAmount cross-fades and rolls the price so the swap reads as smooth instead of a snap.
Build your plan#
Because the component is stateless, the price is just a number you own, so you can drive it from anything, including a set of add-on checkboxes. Tick a box and the total counts up to the new figure instead of snapping: a small useCountUp hook tweens it on requestAnimationFrame (interruptible, and it collapses to an instant settle under prefers-reduced-motion). Each add-on is a full-row label so the whole row is the hit target; the selected row keeps a transparent background, with the brand-coloured price keying off the checkbox's data-state via group-has, so there's no extra state to thread.
API reference#
Pricing
The grid of tiers. Forwards native <div> props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | 2 | 3 | 4 | 3 | Tiers per row at lg; below that the grid falls to two columns, then one. |
PricingTier
One plan card; it owns its tone and shares it with its parts. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "default" | "outline" | "solid" | "muted" | "dashed" | "default" | Transparent, an ink edge, a brand fill with inverse parts, a grey panel, or a dashed edge. |
| featured | boolean | false | Anchors the recommended plan with an ink edge; independent of tone. |
| actionPosition | "bottom" | "top" | "bottom" | bottom pins the CTA so buttons line up across cards; top floats it under the price. |
| size | "md" | "lg" | "md" | lg is the landing-page type scale. |
| asChild | boolean | false | Renders the child (an <a>) as the card, so the whole tier is one link. |
| Data attribute | Description |
|---|---|
| data-featured | Present when featured. |
| data-tone | The tier's tone, when set. |
PricingTierHeader
The name and description block. Drop a Badge beside the name to label a plan. Forwards native <div> props.
PricingTierName
The plan name, a balanced <h3>.
PricingTierDescription
The muted line under the name, a <p>.
PricingPrice
The baseline-aligned price row. Forwards native <div> props.
PricingAmount
The headline figure in tabular-nums, so switching plans never shifts the layout. Text works too, e.g. Custom. Renders a <span>.
PricingPeriod
The muted suffix, e.g. /month. Renders a <span>.
PricingFeatures
The checklist, a <ul>.
PricingFeature
One row, an <li>, with its own leading glyph.
| Prop | Type | Default | Description |
|---|---|---|---|
| included | boolean | true | A success check; false dims the row and shows a neutral minus. |
| icon | React.ReactNode | - | Replaces the leading glyph. |
| hint | React.ReactNode | - | Appends an info glyph whose tooltip carries the detail on hover or focus. |
| hintLabel | string | "More information" | The info glyph's accessible label. |
| Data attribute | Description |
|---|---|
| data-included | Present when the feature is included. |
PricingFeatureGroup
A labelled section of PricingFeature rows under a centred Divider header, for a long, categorised inventory. Rows read at full strength with neutral icons. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | React.ReactNode | - | The header label, e.g. What's inside. Omit for a bare list. |
| icon | React.ReactNode | - | A glyph beside the label. |
PricingTierAction
The CTA band; its direct child stretches to the card's width. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "xl" | "md" | Button size imposed on the CTA, the house 36px by default. The Button's own size wins. |
| Data attribute | Description |
|---|---|
| data-control-size | The imposed size. |
PricingTierBanner
Wraps a PricingTier in a brand panel that peeks as a top label and a thin edge. Works over any tone and stretches to the row in a grid. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | React.ReactNode | - | The banner's text, e.g. Get 40% off. |