VisorVisor
Blocks

Pricing Section

A responsive pricing tier grid with highlighted plan, feature lists, and per-tier CTAs. The canonical complex marketing block for SaaS and product sites.

Preview

Simple, Transparent Pricing

Start free. Scale as you grow. No hidden fees.

Starter

Free

Perfect for side projects and personal use.

  • Up to 3 projects
  • Basic analytics
  • Community support
Most Popular

Pro

$29/mo

For growing teams that need more power.

  • Unlimited projects
  • Advanced analytics
  • Priority support
  • Custom domains

Enterprise

Custom

Tailored solutions for large organizations.

  • Everything in Pro
  • Dedicated support
  • SLA guarantee
  • Custom contracts

Installation

npx visor add --block pricing-section

This copies files into your project:

  • blocks/pricing-section/pricing-section.tsx — the block component
  • blocks/pricing-section/pricing-section.module.css — the styles

Usage

import { PricingSection } from '@/blocks/pricing-section/pricing-section';
import type { PricingTier } from '@/blocks/pricing-section/pricing-section';

const tiers: PricingTier[] = [
  {
    name: "Starter",
    price: "Free",
    features: ["Up to 3 projects", "Basic analytics"],
    buttonText: "Get Started",
    buttonHref: "/signup",
  },
  {
    name: "Pro",
    price: "$29",
    period: "/mo",
    features: ["Unlimited projects", "Advanced analytics", "Priority support"],
    buttonText: "Start Free Trial",
    buttonHref: "/signup/pro",
    highlighted: true,
    badge: "Most Popular",
  },
];

export default function PricingPage() {
  return (
    <PricingSection
      heading="Simple, Transparent Pricing"
      description="No hidden fees. Cancel anytime."
      tiers={tiers}
    />
  );
}

With click callbacks

<PricingSection
  heading="Choose Your Plan"
  tiers={[
    {
      name: "Pro",
      price: "$29",
      period: "/mo",
      features: ["All features", "Priority support"],
      buttonText: "Upgrade Now",
      onButtonClick: () => openUpgradeModal("pro"),
      highlighted: true,
    },
  ]}
/>

With formatted prices

Use the use-currency hook to format prices before passing them as strings:

import { useCurrency } from '@/hooks/use-currency/use-currency';

function PricingWithCurrency() {
  const { format } = useCurrency();

  const tiers: PricingTier[] = [
    {
      name: "Pro",
      price: format(2900), // "$29.00"
      period: "/mo",
      features: ["All features"],
      buttonText: "Get Pro",
      highlighted: true,
    },
  ];

  return <PricingSection tiers={tiers} />;
}

Responsive Behavior

The grid adapts to the number of tiers:

  • Mobile (below 640px): Single column — tiers stack vertically
  • Tablet (640–1023px): Two columns — tiers display side by side
  • Desktop (1024px+): auto-fit with minmax(240px, 1fr) — adapts naturally to tier count without hardcoding a column count

Highlighted Tier

Set highlighted: true on any tier to apply the accent treatment:

  • Border color switches to var(--border-focus) (theme-aware)
  • Background switches to var(--surface-accent-subtle) (theme-aware)
  • Button renders with variant="default" instead of variant="outline"
  • An optional badge string renders above the tier name (e.g., "Most Popular")

Composition

This block composes these Visor components:

  • Card (+ CardHeader, CardContent, CardFooter) — Tier container
  • Badge — Optional "Most Popular" label
  • Button — Per-tier CTA; renders as <a> when buttonHref is provided
  • Heading — Section heading
  • Text — Section description and tier descriptions
  • Check (Phosphor icon) — Feature list check marks

Props

PricingSectionProps

PropTypeRequiredDescription
tiersPricingTier[]YesArray of pricing tier objects to render
headingstringNoOptional section heading above the grid
descriptionstringNoOptional supporting text below the heading
classNamestringNoAdditional CSS class applied to the root <section>

PricingTier

PropTypeRequiredDescription
namestringYesTier name (e.g. "Starter", "Pro", "Enterprise")
pricestringYesFormatted price string (e.g. "$29", "Free", "Custom")
periodstringNoBilling period shown next to price (e.g. "/mo", "/year")
descriptionstringNoShort description below the price
featuresstring[]YesFeature list items, each rendered with a Check icon
buttonTextstringYesCTA button label
buttonHrefstringNoIf provided, renders the button as an anchor tag
onButtonClick() => voidNoClick handler (used when no buttonHref)
highlightedbooleanNoApplies accent border, subtle background, and default button variant
badgestringNoOptional badge label shown above the tier name
colorstringNoPer-tier accent color (CSS color value). Colors the tier name, checkmarks, and hover/highlight glow. Falls back to --accent

About Blocks

Blocks are complete UI patterns made up of multiple Visor components. Unlike individual components, blocks represent larger, composed sections of UI. Blocks are copy-and-own — install them into your project and customize freely.