# Pricing Section v3

> Three-tier pricing cards with a monthly/yearly switch, grouped feature lists, and centered two-line headline.

A shadcn/ui block from the [@uiception](https://uiception.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/pricing-section-v3).

- Page, with a live preview: https://shadcn.exchange/r/uiception/pricing-section-v3
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiception/pricing-section-v3-light-8042c424a285ee44.png
- Registry item: https://uiception.com/r/pricing-section-v3.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiception.com/r/pricing-section-v3.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `button`, `card`, `switch`, `badge`, `label`, `separator`

## Files

### `registry/new-york/blocks/pricing-section-v3/page.tsx`

```tsx
import PricingSectionV3 from "./components/pricing-section-v3"

export default function Page() {
  return <PricingSectionV3 />
}
```

### `registry/new-york/blocks/pricing-section-v3/components/pricing-section-v3.tsx`

```tsx
"use client"

import { useId, useState } from "react"
import { Instrument_Serif } from "next/font/google"

import { Badge } from "@/components/ui/badge"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"

import PricingCard from "./pricing-card"
import { pricingPlans, type BillingCycle } from "../lib/pricing-plans"

const instrumentSerif = Instrument_Serif({
  subsets: ["latin"],
  weight: ["400"],
})

export default function PricingSectionV3() {
  const [billingCycle, setBillingCycle] = useState<BillingCycle>("monthly")
  const switchId = useId()
  const groupId = useId()

  return (
    <section className="py-4 md:py-6 lg:py-8">
      <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        <div className="mx-auto mb-10 max-w-2xl text-center">
          <h2 className={`${instrumentSerif.className} text-4xl leading-[1.15] tracking-tight text-balance md:text-5xl`}>
            <span className="block text-foreground">Simple pricing</span>
            <span className="mt-1 block text-muted-foreground">
              Plans that grow with you
            </span>
          </h2>
          <p className="mx-auto mt-5 max-w-xl text-[0.9375rem] leading-relaxed text-muted-foreground">
            Pick a plan that matches your team size. Every tier includes core
            product access, with higher plans unlocking automation, security,
            and hands-on support.
          </p>
          <div
            className="mt-8 flex flex-wrap items-center justify-center gap-3"
            role="group"
            aria-labelledby={groupId}
          >
            <span id={groupId} className="sr-only">
              Billing frequency
            </span>
            <Label
              className="cursor-pointer text-sm font-medium text-foreground"
              onClick={() => setBillingCycle("monthly")}
            >
              Monthly
            </Label>
            <Switch
              id={switchId}
              checked={billingCycle === "yearly"}
              onCheckedChange={(checked) =>
                setBillingCycle(checked ? "yearly" : "monthly")
              }
              aria-label="Bill yearly"
            />
            <Label
              className="cursor-pointer text-sm font-medium text-foreground"
              onClick={() => setBillingCycle("yearly")}
            >
              Yearly
            </Label>
            <Badge variant="outline">Save 20%</Badge>
          </div>
        </div>
        <ul className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
          {pricingPlans.map((plan) => (
            <PricingCard
              key={plan.id}
              billingCycle={billingCycle}
              plan={plan}
            />
          ))}
        </ul>
      </div>
    </section>
  )
}
```

### `registry/new-york/blocks/pricing-section-v3/components/pricing-card.tsx`

```tsx
import { CheckIcon } from "lucide-react"
import { Instrument_Serif } from "next/font/google"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import type { BillingCycle, PricingPlan } from "../lib/pricing-plans"

const instrumentSerif = Instrument_Serif({
  subsets: ["latin"],
  weight: ["400"],
})

type PricingCardProps = {
  plan: PricingPlan
  billingCycle: BillingCycle
}

export default function PricingCard({ plan, billingCycle }: PricingCardProps) {
  const pricing = billingCycle === "yearly" ? plan.yearly : plan.monthly

  return (
    <li className="min-w-0">
      <Card className="relative h-full gap-0 border border-border py-0 shadow-none ring-0">
        <CardHeader className="gap-3 border-b border-border px-5 pt-5 pb-5">
          <div className="flex items-start justify-between gap-3">
            <div className="flex flex-col gap-1">
              <CardTitle className="text-lg font-semibold tracking-tight text-foreground">
                {plan.name}
              </CardTitle>
              <CardDescription className="text-sm leading-relaxed text-muted-foreground">
                {plan.description}
              </CardDescription>
            </div>
            {plan.popular ? (
              <Badge variant="outline" className="shrink-0">
                Popular
              </Badge>
            ) : null}
          </div>
          <div className="flex flex-col gap-1 pt-1">
            <p className={`${instrumentSerif.className} text-4xl leading-[1.15] tracking-normal text-foreground`}>
              {pricing.price}
            </p>
            <p className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
              {plan.priceSuffix}
            </p>
          </div>
        </CardHeader>
        <CardContent className="flex flex-1 flex-col gap-0 px-5 pt-0 pb-5">
          {plan.featureGroups.map((group, groupIndex) => (
            <div key={group.title}>
              {groupIndex > 0 ? <Separator className="my-4" /> : null}
              <div className={groupIndex === 0 ? "pt-5" : undefined}>
                <p className="mb-3 text-sm text-muted-foreground">{group.title}</p>
                <ul className="flex flex-col gap-2.5">
                  {group.features.map((feature) => (
                    <li
                      key={feature}
                      className="flex items-start gap-2.5 text-sm leading-relaxed text-muted-foreground"
                    >
                      <span className="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground">
                        <CheckIcon aria-hidden className="size-2.5" />
                      </span>
                      <span>{feature}</span>
                    </li>
                  ))}
                </ul>
              </div>
            </div>
          ))}
        </CardContent>
        <CardFooter className="border-t-0 bg-transparent p-5 pt-0">
          <Button className="w-full rounded-full" size="lg">
            {plan.ctaLabel}
          </Button>
        </CardFooter>
      </Card>
    </li>
  )
}
```

### `registry/new-york/blocks/pricing-section-v3/lib/pricing-plans.ts`

```ts
export type BillingCycle = "monthly" | "yearly"

export type FeatureGroup = {
  title: string
  features: string[]
}

export type PricingPlan = {
  id: "core" | "growth" | "pro"
  name: string
  description: string
  popular?: boolean
  monthly: { price: string }
  yearly: { price: string }
  priceSuffix: string
  featureGroups: FeatureGroup[]
  ctaLabel: string
}

export const pricingPlans: PricingPlan[] = [
  {
    id: "core",
    name: "Core",
    description: "For solo creators.",
    monthly: { price: "$29" },
    yearly: { price: "$23" },
    priceSuffix: "Per user/month",
    ctaLabel: "Get Started",
    featureGroups: [
      {
        title: "Workspace",
        features: [
          "3 shared workspaces",
          "Unlimited projects",
          "Basic workflow templates",
        ],
      },
      {
        title: "Support",
        features: [
          "Slack & email integrations",
          "Community templates",
          "Standard email support",
        ],
      },
    ],
  },
  {
    id: "growth",
    name: "Growth",
    description: "For product teams shipping.",
    popular: true,
    monthly: { price: "$59" },
    yearly: { price: "$47" },
    priceSuffix: "Per user/month",
    ctaLabel: "Get Started",
    featureGroups: [
      {
        title: "Workspace",
        features: [
          "Unlimited workspaces",
          "Custom automations",
          "Role-based access",
        ],
      },
      {
        title: "Support",
        features: [
          "API access & webhooks",
          "Advanced analytics",
          "Priority chat support",
        ],
      },
    ],
  },
  {
    id: "pro",
    name: "Pro",
    description: "For organizations scaling operations.",
    monthly: { price: "$119" },
    yearly: { price: "$95" },
    priceSuffix: "Per user/month",
    ctaLabel: "Get Started",
    featureGroups: [
      {
        title: "Workspace",
        features: [
          "Everything in Growth",
          "Audit logs & exports",
          "Admin policies",
        ],
      },
      {
        title: "Support",
        features: [
          "SSO and SAML",
          "Dedicated success contact",
          "Custom onboarding",
        ],
      },
    ],
  },
]
```
