EmpireUI
Get Pro
← Blog7 min read#pricing-card#saas-ui#react-components

Pricing Card Variants: 7 SaaS Designs That Increase Conversions

7 SaaS pricing card designs with real Tailwind code — from glassmorphism to highlighted tiers. See which layouts actually move users toward checkout.

Pricing cards displayed on a laptop screen showing SaaS tier layout with highlighted middle plan

Why Your Pricing Card Is Quietly Killing Sign-Ups

Honestly, most SaaS pricing pages are doing the bare minimum — three columns, a checklist, a button — and wondering why trial conversion is stuck at 3%. The pricing card is the last thing a user sees before they make a decision. That's a lot of weight for a component most teams ship in an afternoon.

A bad layout creates decision paralysis. If all three tiers look equally weighted, users freeze. Visual hierarchy does the thinking for them. The card you want them to pick should feel different — not just labeled 'Most Popular' in a badge nobody reads.

This isn't about tricks. It's about reducing friction at the moment of commitment. And it starts at the component level.

Variant 1 — The Elevated Middle Card (Classic Highlight)

The oldest trick in SaaS pricing is still the most effective: make the middle plan look physically taller. An 8px vertical offset plus a drop shadow pulls the eye immediately. Users read left-to-right, but they click toward the center.

In Tailwind v4.0.2, you pull this off without custom CSS by combining -translate-y-4, shadow-2xl, and a contrasting ring-2 ring-indigo-500. The outer cards sit flat. The Pro card floats. Simple.

// ElevatedPricingCard.tsx
type Tier = {
  name: string;
  price: string;
  features: string[];
  highlight?: boolean;
};

export function PricingCard({ tier }: { tier: Tier }) {
  return (
    <div
      className={`
        relative flex flex-col rounded-2xl p-8 border
        transition-transform duration-200
        ${
          tier.highlight
            ? '-translate-y-4 shadow-2xl ring-2 ring-indigo-500 bg-white dark:bg-zinc-900'
            : 'shadow-sm bg-zinc-50 dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700'
        }
      `}
    >
      {tier.highlight && (
        <span className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-indigo-500 px-3 py-1 text-xs font-semibold text-white">
          Most Popular
        </span>
      )}
      <h3 className="text-lg font-bold text-zinc-900 dark:text-white">{tier.name}</h3>
      <p className="mt-2 text-4xl font-extrabold text-zinc-900 dark:text-white">{tier.price}</p>
      <ul className="mt-6 space-y-3 flex-1">
        {tier.features.map((f) => (
          <li key={f} className="flex items-center gap-2 text-sm text-zinc-600 dark:text-zinc-400">
            <span className="text-indigo-500">✓</span> {f}
          </li>
        ))}
      </ul>
      <button
        className={`mt-8 w-full rounded-xl py-3 text-sm font-semibold ${
          tier.highlight
            ? 'bg-indigo-500 text-white hover:bg-indigo-600'
            : 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200'
        }`}
      >
        Get started
      </button>
    </div>
  );
}

Variant 2 — Glassmorphism Pricing Cards

Glassmorphism pricing cards look expensive. That perception matters when you're selling a premium tier. The frosted-glass effect works by layering backdrop-blur-md over a gradient background with rgba(255,255,255,0.12) as the card fill. The key is contrast — your text must be legible, or the whole thing falls apart.

If you want to understand the underlying theory before implementing, this primer on what glassmorphism actually is is worth reading. Empire UI ships a glassmorphism utility class that handles the blur and border-highlight in one go, so you're not re-inventing backdrop-filter every project.

One gotcha: glassmorphism requires a non-white background to work. If your page background is #ffffff, the blur has nothing to process and the card just looks like a semi-transparent box. Put the pricing section on a gradient or an image, and everything clicks into place.

Variant 3 — Full-Width Horizontal Tier Row

Three columns is the default. But horizontal rows can work better when your tiers have many features and you want a direct comparison format. Think of it as a comparison table that doesn't look like a spreadsheet.

Each row is a tier. Columns are feature categories. Users scan horizontally to see what they're giving up or gaining. This format reduces cognitive load because it maps to how people actually evaluate options — comparing specific attributes, not reading card by card.

The layout risks feeling cold. Fix it by adding a colored left border (border-l-4 border-indigo-500) on the highlighted row and bumping its background to bg-indigo-50 dark:bg-indigo-950/30. The row pops without breaking the grid.

Variant 4 — Toggle Annual/Monthly With Animated Tabs

Every SaaS pricing page needs a billing toggle. Annual plans are where margin lives. But a poorly built toggle feels janky, and jank erodes trust right when you need it most.

The right approach is an animated tab component that slides an indicator between Monthly and Annual labels. No full re-render. No layout shift. Just the prices update with a subtle fade-in via transition-opacity duration-150. Users see the annual discount without the page jumping.

Wire it with a simple useState<'monthly' | 'annual'>('monthly') and derive prices from a map. Don't hardcode two sets of JSX. That's how you end up with price mismatches in production at 2am.

Variant 5 — Dark Gradient Card With Feature Glow

Dark pricing cards signal 'enterprise' or 'premium'. Pair a deep zinc or slate background with a subtle radial gradient glow behind the key feature list and you get something that feels polished without the glassmorphism complexity.

The glow is a single div with bg-gradient-radial from-indigo-500/20 to-transparent positioned absolutely behind the feature list. It needs pointer-events-none and z-0 so it doesn't block clicks. Everything else sits on z-10. Takes four lines of Tailwind.

Want to see how stacked card layouts handle depth and layering? The cards stack component uses a similar z-index approach for the fanned card effect, and a lot of the principles translate directly to pricing card overlays.

Variant 6 — Minimal One-Column Stacked Cards (Mobile-First)

Here's something that gets ignored: most SaaS sign-ups on mobile happen in a single-column stacked layout because that's all the viewport allows. If your three-column desktop pricing card degrades into unreadable 320px columns, you're losing mobile conversions regardless of how good the design is on a 1440p monitor.

The fix is mobile-first from the start. flex flex-col gap-6 on mobile, md:grid md:grid-cols-3 md:gap-8 at the breakpoint. This isn't a responsive afterthought — it's the base case. And on mobile, drop the elevated card trick. Instead, use a full-width border-2 border-indigo-500 to mark the recommended tier.

What's the right gap between cards? 24px (gap-6) on mobile, 32px (gap-8) on desktop. Enough breathing room without making the page feel sparse. These numbers feel right across device sizes.

Variant 7 — Interactive Card With Seat/Usage Slider

Usage-based pricing is everywhere now. If your product charges per seat, per API call, or per GB, a static pricing card lies to the user. They don't know what they'll pay. That uncertainty is a conversion killer.

An interactive card with a slider that updates the displayed price in real time solves this. Keep the math client-side. const price = basePrice + seats * pricePerSeat — that's it. Display it with a useEffect that debounces the calculation by 50ms so it doesn't fire on every pixel of drag.

Pair this with an animated button for the CTA that pulses or shifts color once the user has interacted with the slider. It's a subtle signal: 'you've configured this plan, now commit to it.' Small cues like that move the needle on trial sign-up rates.

Choosing the Right Variant for Your SaaS

So which one should you ship? It depends on your pricing model more than your brand preferences. Three tiers with a clear middle option? Go with the elevated classic or the glassmorphism variant. Usage-based or seat-based? The interactive slider card isn't optional — it's the only honest representation of your pricing.

If you're an agency building pricing pages for multiple clients, the horizontal row variant scales across different feature sets without custom design work for each. It's table-based thinking in card clothing.

Don't A/B test all seven at once. Pick one, ship it, measure it for three to four weeks. Change the highlighted tier background. Change the CTA button copy. Small changes, one at a time. That's how you actually learn what's working.

FAQ

How do I handle dark mode in a pricing card without duplicating all my color classes?

Use Tailwind's dark: variant consistently and define your color palette as CSS custom properties. For example, --card-bg: #ffffff in :root and --card-bg: #18181b in .dark. Reference bg-[var(--card-bg)] in your component. You avoid duplicating every color class and dark mode just works via the .dark class on your root element.

What's the best way to animate price changes when switching between monthly and annual billing?

A CSS opacity transition is enough. Wrap the price in a span and toggle a class that sets opacity: 0 for 150ms, update the value, then restore opacity: 1. In React, use a key prop on the price element — changing the key forces a remount, which triggers CSS animations cleanly without any animation library.

Should the pricing card CTA say 'Get started' or 'Start free trial'?

Specificity wins. 'Start free trial' converts better than 'Get started' because it answers the implicit question: 'Do I have to pay right now?' If your trial is gated or requires a card, say 'Start trial' and disclose the requirement clearly. Ambiguity at the CTA is a silent conversion killer.

How do I make the glassmorphism effect look good on Safari?

Safari requires -webkit-backdrop-filter alongside the standard backdrop-filter property. In Tailwind v4.0.2 this is handled automatically by the PostCSS autoprefixer, but if you're writing inline styles or custom CSS you need both declarations. Also test on Safari iOS — the blur performance can be rough on older devices, so cap your blur value at 12px or 16px.

My pricing card has 10+ features per tier. How do I handle overflow without the card growing too tall?

Split features into two categories: included (show all) and available as add-ons (show count with a tooltip). Most users don't read past the fifth feature anyway. If you must show all of them, add max-h-48 overflow-y-auto to the feature list with a subtle fade-out mask at the bottom via a ::after pseudo-element with a gradient. This signals 'there's more' without expanding the card layout.

Is it worth building pricing cards as a compound component pattern in React?

Yes, if you're shipping multiple pricing sections across a product. A compound pattern with <PricingCard>, <PricingCard.Header>, <PricingCard.Features>, and <PricingCard.CTA> gives you full layout control at the usage site without prop drilling a dozen options. For a single pricing page, it's overkill — a single component with a well-typed tier prop is fine.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

SaaS UI Patterns for 2027: Dashboard, Billing, OnboardingChip & Tag Input: Building Gmail-Style Tag Components in ReactTailwind Pricing Page: SaaS Tier Comparison DesignDark UI Patterns for SaaS: Navigation, Sidebars, Data Tables