All posts
27 min read

68 shadcn Pricing Blocks: Tier Grids, Calculators, Comparison Tables, and the Psychology Underneath Them

The pricing category is sixty-eight production-ready blocks built on shadcn/ui: tier grids with billing toggles, per-seat and usage calculators, comparison tables, credit packs, service ranges, and enterprise panels. Plus what the research actually says about anchoring, decoys and choice overload, the exact point where pricing UX turns deceptive, and the law that now draws that line.

pricingshadcnreactnextjsux

Every other section of a marketing site describes the product. The pricing page briefly is the product. It is the one screen where a layout decision moves money in both directions: a clear table closes a deal the rest of the site opened, and a clever table closes one that comes back as a refund, a chargeback, or a one-star review about the annual charge nobody saw coming.

That is why pricing is the section people rewrite the most and finish the least. The design problem looks like a card grid. The real problem is that you are asking a stranger to translate their situation into your unit of value, in about ninety seconds, using words you invented.

The pricing category on Beste UI is now sixty-eight blocks. This post is the map: the shapes a price can take on screen and which block implements each one, then the part nobody puts in a component library, which is what the research says about how people read prices, where that knowledge turns into manipulation, and the surprisingly precise legal line that now sits underneath it.

A pricing page is a model of your business, rendered

Before any of this is a design question it is an arithmetic question: what is the unit you charge for, and does it grow with the value the customer gets?

Seats, requests, sites, seconds of video, projects, revenue processed. Ramanujam and Tacke call this the value metric in Monetizing Innovation, and their argument is the one worth stealing: the pricing conversation belongs at the start of the product, not at the end. A page cannot rescue a unit that does not track value. If you charge per seat for a tool where the value comes from one power user and forty read-only viewers, no card grid saves you. Your customers will simply share a login, which is a design review you receive as a support ticket.

Once the unit is right, the page has three jobs, in this order:

  1. Tell people what it costs. Sounds trivial. A great many B2B pricing pages fail it on purpose.
  2. Help a reader find themselves. Nobody reads a pricing table left to right. They scan for the row that describes their situation and then read only that column.
  3. Not lie. Including the passive lies: the price that is monthly only if you pay for the year, the seat count that is a minimum, the fee that appears at checkout.

Every block below is an implementation of jobs one and two. Job three is on you, and the last third of this post is about how to keep it.

What installs

Each block is a single React component. The CLI resolves the whole tree in one command:

bash
npx shadcn add "https://ui.beste.co/r/pricing13?email=YOUR_EMAIL&license_key=YOUR_KEY"

That lands the section at components/beste/block/pricing13.tsx, plus whatever shadcn/ui primitives it composes (button, badge, slider, tabs, accordion, tooltip), installed only if you do not already have them. No runtime dependency on us, no theme provider, no license check inside your bundle. It is TSX in your repo, which is the whole point of the copy-paste model.

If you are on Base UI rather than Radix, swap /r/ for /r-base/ and you get the Base UI build of the same tree. Colors, radii and the emphasis on the recommended tier all resolve through your CSS variables, so the blocks arrive wearing your brand rather than ours. The anatomy of a shadcn theme covers those tokens if you are still shaping them.

The shapes a price takes

Sixty-eight blocks is not sixty-eight ideas. It is six shapes, each with variations for the design language and the amount of detail you owe the reader. Pick the shape by looking at your own billing model, not at which screenshot is prettiest.

One: tier grids, and the problem of the middle column

The three-card grid is the default for a reason. It is compact, it scans in a diagonal, and it maps cleanly onto small, medium and large.

Three Tiers With Billing Toggle
View block
Loading preview…
pricing71: three tiers where the monthly and yearly toggle swaps both the figure and the billing note on every card, so the small print can never disagree with the big number.

Two details in that block are worth copying regardless of which one you install. The toggle changes the period label at the same time it changes the price, which sounds obvious until you audit real pricing pages and find the yearly figure sitting above the word "month" with the explanation somewhere near the footer. And the recommended tier is marked with an outline in the accent color rather than by being physically taller, so the three cards keep one baseline at every width, which is the difference between a grid that survives a narrow container and one that stacks into a random order.

The middle column is also where the whole page can quietly turn into a trick, and the research section below is about exactly that. For now, one rule: mark a tier as recommended because it is the one most customers should buy, and be able to say why in a sentence.

If your plans differ by commitment rather than by feature set, pricing23 is the honest version of annual billing: it shows the yearly figure, the per-month equivalent, and the savings in the same card, so nobody has to divide by twelve to check your claim.

BlockShapeUse it when
pricing60Three tiers, emphasized middleThe default, when plans differ by size
pricing21Minimal three-tierEarly product, short feature lists
pricing6Three tiers with monthly and yearly toggleTwo billing cycles, one grid
pricing61Toggle variant with feature listsSame, with longer inclusion lists
pricing71Toggle that swaps figure and billing noteWhen the small print must move with the price
pricing7Two tiers, clear upgrade pathFreemium with one paid plan
pricing10Four horizontal cardsFour plans that still need side-by-side scanning
pricing23Annual price with per-month equivalentAnnual-first pricing, shown honestly
pricing28Tabbed monthly, quarterly, annualThree or more commitment options
pricing40Compact button-toggle selectorFew options, tight vertical space
pricing18Individual and Team tabsOne product, two audiences, different features
pricing24Tiers by funding stageStartup programs, bootstrapped to Series B
pricing34Radio-card selectionCheckout and in-app plan switching
pricing51Icon-highlighted key featuresWhen one benefit per plan carries the decision
pricing59Tabbed membership with summary panelMemberships and clubs
pricing63Two contrasting full panelsTwo engagement models, not two sizes
pricing65Monochrome editorial tiersStudios and brand-led sites
pricing69Boxless ruled columnsEditorial layouts where cards feel loud

Two: one plan, and one plan plus parts

A single plan is the strongest pricing page you can ship, and the hardest to earn. It says the product does one thing and you know who it is for.

Single Plan With Full Inclusion List
View block
Loading preview…
pricing72: one plan, an oversized figure, two actions, and everything included listed as three hairline groups underneath. No comparison, because there is nothing to compare.

The interesting variants are the ones that keep a single base price and let the reader build the rest. pricing31, pricing32, pricing39 and pricing41 are the same mechanic in four skins: a base figure, a set of modules, a total that recomputes as you toggle. They work when your add-ons are genuinely optional. They fail, loudly, when the add-ons are the product and the base price is a decoy for a number nobody actually pays.

The rule we would put on a wall: an add-on toggle must start switched off. A prechecked upsell is not a configurator, it is a bill you wrote for someone else, and the section on deception below explains exactly which line it crosses.

BlockShapeUse it when
pricing16Minimal single planOne product, one price
pricing72Single plan with full inclusion listLong feature list, no tiers
pricing66Single retainer cardStudios and ongoing engagements
pricing42Features grouped into categoriesTwenty or more inclusions
pricing17Plan plus FAQObjections that arrive before the click
pricing29Plan plus FAQ accordionSame, with more questions
pricing9Plan with selectable add-onsSimple modular upsells
pricing64Spotlight plan beside add-on listOne clear plan, several modules
pricing37Toggle-group add-onsA la carte selection
pricing41Switch-toggled add-onsFeature upgrades on a base plan
pricing31Base plan plus dynamic totalModular products
pricing32Modular builder with live totalEnterprise packages
pricing39Checkbox feature builderCustomizable service packages
pricing55Multi-step plan builderConfigurations too long for one screen
pricing56Sticky summary sidebarLong pages where the total must stay visible
pricing58Scrollable features beside a slogan panelImage-led brand pages

Three: the comparison table

Once you have more than about six differences per plan, cards stop working and a table starts. This is the shape people get wrong most often, because a comparison table is a real data table with real semantics, not a grid of divs that happens to look aligned.

Feature Comparison Table
View block
Loading preview…
pricing8: plans as columns, features grouped by category, and cells that carry either a value or a state. The emphasized column keeps its tint through every row so your eye does not lose the plan it is following.

Three things make the difference between a table people read and a table people skip:

BlockShapeUse it when
pricing8Grouped multi-plan matrixComplex products, three or more tiers
pricing62Plans as columns, check and dash cellsStraight feature parity comparison
pricing67Monochrome ruled matrixEditorial and brand-led sites
pricing15Free versus Pro, two columnsFreemium upgrade page
pricing44Collapsible feature groupsMany features, small viewport
pricing49Tabs per plan, features by categoryMobile-first comparison
pricing33Tiers with per-feature tooltipsJargon that needs a definition
pricing43Hover cards with expanded detailDense information, uncluttered layout

Four: calculators, for when the price is a function

Usage-based and per-seat pricing changes the job of the page. The number is no longer something you display, it is something the reader computes about themselves, and your only real task is to make that computation fast and non-scary.

Per-Seat Calculator
View block
Loading preview…
pricing13: seats on a slider, the per-seat rate, the discount band it lands in, and the monthly total, all in one frame. The bands on the right light up as you cross them, so the next discount is visible before you need it.

The arithmetic in that block is deliberately readable: it picks the highest volume band whose minSeats you have crossed, applies the percentage, multiplies by seats. Because the whole thing is props, your real rate card lives in one object:

tsx
import { Pricing13 } from "@/components/beste/block/pricing13";

export default function TeamPricing() {
  return (
    <Pricing13
      pricePerSeat={14}
      volumeDiscounts={[
        { minSeats: 10, discount: 10 },
        { minSeats: 25, discount: 15 },
        { minSeats: 50, discount: 20 },
      ]}
      features={["Everything in Pro", "SAML SSO", "Audit log", "Priority support"]}
    />
  );
}

pricing11 does the same for metered products: the slider moves across usage breakpoints rather than a continuous range, which is the honest shape when your rate card actually has steps in it. A continuous slider over a stepped price is a small lie that becomes an angry invoice.

Two rules for any calculator you ship. Call the output an estimate and mean it, including whatever your billing system does about proration and overage. And show the ceiling: pricing45 and pricing57 both display the full band table beside the calculator, so an enterprise reader can see where the curve is going without dragging to the end.

BlockShapeUse it when
pricing13Per-seat slider with volume discountsTeam tools priced per user
pricing68Seat slider with cost breakdown panelSame, with an itemized summary
pricing11Usage slider across tier breakpointsAPIs and consumption pricing
pricing27Tiered rate per requestDeveloper infrastructure
pricing57Volume discount tableRate card shown as a table, not a toy
pricing45Enterprise volume calculatorLarge deployments, negotiated bands
pricing73Per-site bands with live group totalMulti-location businesses
pricing30Budget slider that matches a planThe reader knows their budget, not their needs
pricing26ROI calculatorEnterprise sales where value needs a number

An ROI calculator is a promise

pricing26 is the block to be most careful with. A projection that assumes your best customer's results, presented as the reader's results, is a sales claim with a spreadsheet costume. Seed it with conservative inputs, label the assumptions, and let the reader change them.

Five: when it is not a subscription

Not everything is SaaS, and forcing a studio, an agency or a one-time product into a three-tier grid is how a pricing page starts sounding like someone else's business.

Project Estimate Ledger
View block
Loading preview…
pricing70: project work priced as ranges. Each row is a kind of project, what it usually includes, how long it takes, and where the number starts. The note under the table names the two things that move the price more than anything the studio does.

That block is a small argument in favor of published ranges. Agencies avoid them because every project is different, which is true and also exactly why the reader needs a starting point. A range plus the conditions that move it filters out the wrong enquiries before either side spends a call on them.

Credits (pricing19) deserve a note of their own, because prepaid balances change how spending feels. Prelec and Loewenstein's work on the pain of paying is the reason: decoupling payment from consumption makes the second act painless. That is genuinely good design for an AI product where every action costs fractions of a cent and a per-request invoice would be unreadable. It becomes a trap the moment credits expire quietly or the balance is hard to find. If you sell credits, show the cost per action, as that block does, and never let expiry be the business model.

BlockShapeUse it when
pricing70Project estimate ledger with rangesStudios and agencies, published starting points
pricing25Fixed-price service packagesProductized services with timelines
pricing36Service tiers with delivery windowsConsulting and professional services
pricing19Credit packs with usage examplesAI tools and pay-as-you-go
pricing14Lifetime deal with original priceOne-time purchase, genuine launch offer
pricing22Pay what you wantIndie tools, open source, creator products
pricing53Bundle pricing with savingsProduct suites
pricing12Enterprise panel with contact salesDeals that are actually negotiated

On that last one: "Contact sales" is legitimate when the price genuinely depends on a conversation, and a tax on your reader when it does not. If you use pricing12, give it a floor. "Enterprise plans start at $2,000 per month" costs you nothing and saves both sides a scheduling link.

Six: the commerce layer around the price

The remaining blocks are not layouts, they are the things a pricing page grows once real money runs through it: currency selection, guarantees, payment methods, social proof, loading states, promo codes.

Regional Currency Pricing
View block
Loading preview…
pricing35: a region selector that changes the currency and the price together. Regional pricing is a product decision before it is a component, but when you make it, the page has to say which market it is quoting.

Two of these deserve caution rather than enthusiasm.

A promo code field is expensive. Baymard's checkout usability research has documented for years what an empty coupon box does to a committed buyer: it introduces the possibility that everyone else is paying less, and sends people out of your flow to search for a code they may never come back with. If you run coupons, use pricing52 collapsed behind a link, or apply the discount automatically from the campaign link and show it as an applied line rather than an empty invitation.

And pricing47, the countdown, is the block most likely to be misused, which is the subject of the next two sections.

BlockShapeUse it when
pricing35Region and currency selectorGenuine regional pricing
pricing50Accepted payment methodsCheckout-adjacent pages
pricing48Guarantee and refund badgesA refund policy you actually honor
pricing20Plan with embedded testimonialSocial proof at the decision point
pricing38Ratings and review countsMarketplaces and consumer products
pricing52Promo code inputCampaigns and affiliate programs
pricing47Countdown on a promotional priceA deadline that is real
pricing46Skeleton loading statePrices fetched from your billing provider
pricing54Plan recommendation quizPlans too different to compare in a row

pricing54 is the interesting one in that list. When your plans differ by kind rather than by size, a short set of questions beats a wide table, and the result explains why it picked what it picked. It is the same idea we built the questionnaire blocks on: ask one question, then use the answer.

What the research says about how people read prices

Pricing psychology has a bad reputation because it is usually delivered as a list of tricks. The findings themselves are older, duller and more useful than the listicles suggest. They describe how people read numbers. They are not permission to manufacture the reading.

Prices are read relative to something. Tversky and Kahneman's work on anchoring is the base finding: an arbitrary first number pulls every judgement that follows. On a pricing page the anchor is whatever the eye lands on first, which is usually your highest tier or your crossed-out original. This is unavoidable, not optional. You are choosing an anchor whether or not you think about it, so choose one you can defend when a customer asks.

A third option changes how the other two look. Huber, Payne and Puto's asymmetric dominance effect showed that adding an option nobody picks changes which of the remaining options people do pick. The famous illustration is the Economist subscription bundle popularized by Dan Ariely, where a print-only offer that almost nobody chose existed to make the print-plus-web bundle look obvious. The line here is thin but real. A tier that exists because some customers genuinely buy it is a plan. A tier engineered to be visibly worse so the neighbouring column looks generous is a prop, and props are the thing readers eventually notice.

The left digit does most of the work. Thomas and Morwitz's left-digit effect is why $29 and $30 feel further apart than one dollar. Nothing dishonest about it, and nothing magical either. It matters most at the boundary and is noise everywhere else. Spend the meeting on the value metric instead.

Framing a saving as a loss changes behavior more than framing it as a gain. That is prospect theory in one line, and it is why "save $58" outperforms "pay $290". Fine, as long as the $58 is real. If your annual price is simply your monthly price times twelve, do not put a savings badge on the toggle.

More options are not obviously better, and not obviously worse. Iyengar and Lepper's jam study is the one everyone cites for choice overload. It is worth knowing that the meta-analysis by Scheibehenne, Greifeneder and Todd found the aggregate effect close to zero and highly dependent on context. So the honest version of the advice is not "three plans is the magic number". It is that choice hurts when the reader cannot tell the options apart. Four clearly differentiated plans beat three plans separated by adjectives. If you cannot write one sentence explaining who each plan is for, that is your signal, not the count.

The useful summary: these effects describe the room your page is read in. Designing with them means making your real offer legible. Designing against them means engineering a misreading.

Where it turns into deception

Harry Brignull named these patterns in 2010 and still catalogues them at deceptive.design. The measurement came later: Mathur and colleagues crawled 11,000 shopping sites for Dark Patterns at Scale and found 1,818 instances across roughly 11 percent of sites, sorted into a taxonomy of urgency, scarcity, misdirection, sneaking, obstruction, forced action and social proof. Their method is the part designers should remember: they revisited pages every four hours for five days, which is how you distinguish a deadline from a decoration. A countdown that resets when you come back is not a deadline.

Here is what those categories look like specifically on a pricing page, and what to do instead.

The patternHow it shows up in pricingThe honest version
Fake urgencyA countdown that restarts on every visit or every sessionA deadline stored server-side, the same for everyone, that expires and stays expired
Fake scarcity"Only 3 seats left at this price" with no inventory behind itSay nothing, or state a real cap and honor it
Reference pricingA crossed-out price the product never sold atCross out a price you actually charged, recently
SneakingAdd-ons prechecked, plan preselected as the expensive oneEverything off by default, the reader adds what they want
MisdirectionMonthly figure shown while the annual toggle is silently onThe period label changes with the number, every time
Hidden costsSetup fees, per-transaction fees, mandatory onboarding revealed at checkoutTotal cost of the first year visible on the pricing page
Confirmshaming"No thanks, I like paying too much"A neutral decline
ObstructionSign up in two clicks, cancel in nine and a phone callCancel where you subscribed, in the same number of steps
Forced continuityA trial that converts with no noticeA reminder before the first charge, sent by you

Two of those deserve implementation detail, because they are exactly where our own blocks could be misused.

The countdown. pricing47 takes an endTime prop and counts down to it in an effect. Its demo default is computed from Date.now(), which produces a deadline three days from whenever the page happens to render. That is precisely the evergreen countdown you must not ship, and it is fine in a demo for the same reason a lorem ipsum paragraph is fine: nobody is being sold anything. When you wire it up, endTime should be a fixed timestamp that came from your campaign configuration, identical for every visitor, and the section should render an ended state rather than quietly restarting. If a deadline is worth putting on the page, it is worth letting it pass.

The crossed-out price. pricing14 and pricing47 both display an original price beside a discounted one. That comparison is a factual claim about your own history. Make it one.

There is also a business argument, and it is stronger than the ethical one is usually allowed to be. Deceptive pricing does not fail at the moment of purchase, it succeeds there. It fails four weeks later in refund requests, chargeback ratios, involuntary churn and the specific kind of review that mentions your billing rather than your product. You are borrowing conversion from your future retention at a bad rate.

The line the law now draws

This stopped being a matter of taste. If you sell into the EU or the US, several of the patterns above are already illegal, and the drafting is unusually specific.

None of this requires a lawyer for a pricing page. It requires that the page say what you will charge, when, and how to stop.

The technical part

The design questions are the hard ones, but pricing sections have a small set of implementation traps that are worth naming once.

Keep the client boundary around the calculator, not the page. A tier grid is static markup and should render on the server. A seat slider needs state. The blocks that compute are marked "use client", and if you compose a section around them, keep the interactive part as a child rather than making the whole route client-rendered. This is the most common accidental deopt in a Next.js marketing site, and where the use client boundary actually goes covers the mental model.

Format money deterministically. Called with no argument, toLocaleString() and Intl.NumberFormat resolve against the runtime's own locale and ICU data, so a server in one region and a browser in another can disagree about whether a thousand is written 1,000 or 1.000. On a client component that also renders on the server, that disagreement is a hydration error on your highest-intent page. Most of the blocks take prices as preformatted strings, which sidesteps it entirely. Where one does format a number for you, pin the locale: pricing68 calls toLocaleString("en-US") for exactly this reason, and that is the line to copy into any calculator you extend.

Prices belong in one place, and it is not the JSX. Every block takes its figures as props, so keep the rate card in a single module, or read it from your billing provider. If you are on Stripe prices or Polar, fetch on the server, cache it, and use pricing46 for the loading state if the fetch is client-side. A price that exists as a literal in a component is a price that will be wrong in one of the three places it appears.

A comparison table should be a table. pricing8 uses real thead, th and td elements, with a minimum width and horizontal scroll instead of a collapse that reflows the cells into an order nobody can follow. Real table markup is what lets assistive technology tie a cell back to its plan, and adding scope="col" to the plan headers, plus promoting each feature-name cell to a th with scope="row", makes that association explicit rather than inferred. If you rebuild the same layout out of divs, you have made a picture of a table.

Give state cells an accessible name. This one is on you when you wire your own values in. A Check icon and a Minus icon are decoration to a screen reader, and a row that reads "Guest access" followed by silence tells the listener nothing. Add an sr-only span of "Included" or "Not included" beside the icon. You own the file, so this is a two-line edit rather than a feature request.

Announce the number that changes. When a slider recomputes a total, sighted users see it move and everyone else gets nothing. Wrap the total in an aria-live="polite" region, and make sure the slider itself carries a label. For countdowns, do not announce every second: update a polite region once a minute, or provide the deadline as text and let the timer be decorative. The WCAG timing guidance is worth reading before you put a clock on a page.

Say what is included in the number. Tax display is a jurisdiction question, not a design one: consumer prices in the EU are quoted including VAT, US prices typically exclude sales tax, and B2B pricing pages usually quote excluding tax with a note. Pick the convention for your market and put it in the block's period label, where it will be read, rather than in a footer, where it will not.

Choosing one

If you already know your billing model, this is a short decision:

The whole category sits at /blocks/pricing, and every block has a live full-screen preview and its full source on its own page. If you drive an agent, the MCP server can search all of them by intent and compose the section for you.

Then the part no component can do for you. Read your finished page as somebody who has never used your product, and answer three questions out loud: what will I be charged, when will I be charged it, and how do I stop. If the page answers all three without you explaining anything, the psychology takes care of itself. If it does not, no amount of layout will fix it, and the countdown will only make it worse.

Ship the pricing page this afternoon

All sixty-eight pricing blocks, and every other Pro block, are included in one license. Install from the CLI, own the source, and change the numbers in one file.

Get Pro
Markdown version