---
title: Pricing
description: Pricing layouts for plans, billing, and feature comparisons.
group: Blocks
order: 35
---

Pricing layouts that make the differences between plans easy to read. Replace the numbers and features with your own.

## Three tiers

Three familiar plan cards. The popular option uses your primary color, with checkmarks and bottom-aligned actions.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

type Pricing01Props = {
  className?: string;
};

function CheckIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      aria-hidden="true"
      className={className}
    >
      <path
        d="M5 12.5l4.5 4.5L19 7"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

const PLANS = [
  {
    name: "Hobby",
    price: "$0",
    period: "/mo",
    description: "For side projects and trying the kit.",
    features: ["3 projects", "Basic blocks", "Community support"],
    cta: "Start free",
    featured: false,
  },
  {
    name: "Pro",
    price: "$29",
    period: "/mo",
    description: "For teams shipping real product surfaces.",
    features: [
      "Unlimited projects",
      "All blocks",
      "Priority support",
      "Team seats",
    ],
    cta: "Get Pro",
    featured: true,
  },
  {
    name: "Enterprise",
    price: "Custom",
    period: "",
    description: "Security, SLAs, and dedicated onboarding.",
    features: ["SSO & audit logs", "Custom contracts", "Dedicated support"],
    cta: "Talk to us",
    featured: false,
  },
] as const;

export function Pricing01({ className }: Pricing01Props) {
  return (
    <section className={cn("@container w-full bg-background", className)}>
      <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">
        <div className="mx-auto max-w-md text-center">
          <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">
            Simple pricing
          </h2>
          <p className="mt-2 text-sm tracking-wide text-muted-foreground">
            Pick a plan and ship. Upgrade when the team grows.
          </p>
        </div>
        <ul className="mt-8 grid grid-cols-1 gap-3 @[40rem]:grid-cols-3">
          {PLANS.map((plan) => (
            <li
              key={plan.name}
              className={cn(
                "flex flex-col rounded-xl p-5",
                plan.featured
                  ? "bg-primary text-primary-foreground"
                  : "bg-muted text-foreground",
              )}
            >
              <div className="flex items-baseline justify-between gap-2">
                <p className="text-sm font-medium tracking-wide">{plan.name}</p>
                {plan.featured ? (
                  <span className="rounded-md bg-primary-foreground px-1.5 py-0.5 text-[11px] tracking-wide text-primary">
                    Popular
                  </span>
                ) : null}
              </div>
              <p className="mt-3 flex items-baseline gap-0.5">
                <span className="text-3xl font-medium tracking-tight">
                  {plan.price}
                </span>
                {plan.period ? (
                  <span
                    className={cn(
                      "text-xs tracking-wide",
                      plan.featured
                        ? "text-primary-foreground/70"
                        : "text-muted-foreground",
                    )}
                  >
                    {plan.period}
                  </span>
                ) : null}
              </p>
              <p
                className={cn(
                  "mt-2 text-xs tracking-wide",
                  plan.featured
                    ? "text-primary-foreground/70"
                    : "text-muted-foreground",
                )}
              >
                {plan.description}
              </p>
              <ul className="mt-5 flex flex-col gap-2">
                {plan.features.map((feature) => (
                  <li
                    key={feature}
                    className={cn(
                      "flex items-start gap-2 text-xs tracking-wide",
                      plan.featured
                        ? "text-primary-foreground/85"
                        : "text-muted-foreground",
                    )}
                  >
                    <CheckIcon
                      className={cn(
                        "mt-0.5 size-3.5 shrink-0",
                        plan.featured
                          ? "text-primary-foreground"
                          : "text-foreground",
                      )}
                    />
                    <span>{feature}</span>
                  </li>
                ))}
              </ul>
              <div className="mt-auto pt-6">
                <Button
                  type="button"
                  size="sm"
                  className="w-full"
                  variant={plan.featured ? "secondary" : "default"}
                >
                  {plan.cta}
                </Button>
              </div>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add pricing-01
```

### Usage

```tsx
import { Pricing01 } from "@/components/blocks/pricing/pricing-01"
```

```tsx
<Pricing01 />
```

## Billing toggle

A monthly and yearly toggle above three equal-height cards. Prices roll with a number ticker when billing changes.

```tsx
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

type Pricing02Props = {
  className?: string;
};

const PLANS = [
  {
    name: "Starter",
    monthly: 19,
    yearly: 15,
    description: "Solo builders and early launches.",
    features: ["10 projects", "Core blocks", "Email support"],
  },
  {
    name: "Team",
    monthly: 49,
    yearly: 39,
    description: "Growing product and design teams.",
    features: [
      "Unlimited projects",
      "All blocks",
      "Shared library",
      "Slack support",
    ],
  },
  {
    name: "Business",
    monthly: 99,
    yearly: 79,
    description: "Orgs that need control and scale.",
    features: ["Everything in Team", "SSO", "Audit log", "Priority SLA"],
  },
] as const;

function TickerDigit({ digit, delay }: { digit: string; delay: number }) {
  const n = Number(digit);

  return (
    <span className="relative inline-block h-[1em] w-[0.62em] overflow-hidden">
      <span
        aria-hidden
        className="absolute inset-x-0 top-0 flex flex-col will-change-transform"
        style={{
          transform: `translateY(${-n}em)`,
          transition: `transform 700ms cubic-bezier(0.22, 1, 0.36, 1) ${delay}ms`,
        }}
      >
        {Array.from({ length: 10 }, (_, i) => (
          <span
            key={i}
            className="flex h-[1em] items-center justify-center tabular-nums"
          >
            {i}
          </span>
        ))}
      </span>
      <span className="invisible tabular-nums">{digit}</span>
    </span>
  );
}

function PriceTicker({ value }: { value: number }) {
  const digits = String(value).split("");

  return (
    <span className="inline-flex items-baseline">
      <span className="mr-0.5">$</span>
      <span className="inline-flex" aria-label={`${value}`}>
        {digits.map((digit, index) => (
          <TickerDigit key={index} digit={digit} delay={index * 60} />
        ))}
      </span>
    </span>
  );
}

export function Pricing02({ className }: Pricing02Props) {
  const [yearly, setYearly] = useState(true);

  return (
    <section className={cn("@container w-full bg-background", className)}>
      <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">
        <div className="flex flex-col items-center gap-5 text-center">
          <div className="max-w-md">
            <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">
              Pay monthly or yearly
            </h2>
            <p className="mt-2 text-sm tracking-wide text-muted-foreground">
              Yearly billing saves about 20% on every plan.
            </p>
          </div>
          <div className="inline-flex rounded-lg bg-muted p-1">
            <button
              type="button"
              onClick={() => setYearly(false)}
              className={cn(
                "rounded-md px-3 py-1.5 text-xs tracking-wide",
                !yearly
                  ? "bg-background text-foreground"
                  : "text-muted-foreground",
              )}
            >
              Monthly
            </button>
            <button
              type="button"
              onClick={() => setYearly(true)}
              className={cn(
                "rounded-md px-3 py-1.5 text-xs tracking-wide",
                yearly
                  ? "bg-background text-foreground"
                  : "text-muted-foreground",
              )}
            >
              Yearly
            </button>
          </div>
        </div>

        <ul className="mt-8 grid grid-cols-1 items-stretch gap-3 @[40rem]:grid-cols-3">
          {PLANS.map((plan) => {
            const price = yearly ? plan.yearly : plan.monthly;

            return (
              <li
                key={plan.name}
                className="flex h-full flex-col rounded-xl bg-muted p-5"
              >
                <p className="text-sm font-medium tracking-wide text-foreground">
                  {plan.name}
                </p>
                <p className="mt-3 flex items-baseline gap-1 text-3xl font-medium tracking-tight text-foreground">
                  <PriceTicker value={price} />
                  <span className="text-xs font-normal tracking-wide text-muted-foreground">
                    /mo
                  </span>
                </p>
                <p className="mt-1 text-[11px] tracking-wide text-muted-foreground">
                  {yearly ? "Billed yearly" : "Billed monthly"}
                </p>
                <p className="mt-3 text-xs tracking-wide text-muted-foreground">
                  {plan.description}
                </p>
                <ul className="mt-5 flex flex-col gap-2">
                  {plan.features.map((feature) => (
                    <li
                      key={feature}
                      className="text-xs tracking-wide text-muted-foreground"
                    >
                      {feature}
                    </li>
                  ))}
                </ul>
                <div className="mt-auto pt-6">
                  <Button type="button" size="sm" className="w-full">
                    Choose {plan.name}
                  </Button>
                </div>
              </li>
            );
          })}
        </ul>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add pricing-02
```

### Usage

```tsx
import { Pricing02 } from "@/components/blocks/pricing/pricing-02"
```

```tsx
<Pricing02 />
```

## Comparison table

A feature matrix across three plans, with the Pro column filled in your primary color.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

type Pricing03Props = {
  className?: string;
};

const FEATURES = [
  { name: "Projects", hobby: "3", pro: "Unlimited", business: "Unlimited" },
  { name: "Blocks", hobby: "Core", pro: "All", business: "All + private" },
  { name: "Seats", hobby: "1", pro: "10", business: "Unlimited" },
  { name: "Support", hobby: "Community", pro: "Priority", business: "Dedicated" },
  { name: "SSO", hobby: "—", pro: "—", business: "Included" },
  { name: "Audit log", hobby: "—", pro: "—", business: "Included" },
] as const;

const PLANS = [
  { key: "hobby", name: "Hobby", price: "$0", cta: "Start free", featured: false },
  { key: "pro", name: "Pro", price: "$29", cta: "Get Pro", featured: true },
  { key: "business", name: "Business", price: "$99", cta: "Get Business", featured: false },
] as const;

/** Solid column fill + hairline drawn above it (not a translucent border overlay). */
const rowRule =
  "before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:z-10 before:h-px before:bg-border";
const featuredRowRule =
  "before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:z-10 before:h-px before:bg-primary-foreground/25";

export function Pricing03({ className }: Pricing03Props) {
  return (
    <section className={cn("@container w-full bg-background", className)}>
      <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">
        <div className="mx-auto max-w-md text-center">
          <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">
            Compare plans
          </h2>
          <p className="mt-2 text-sm tracking-wide text-muted-foreground">
            Everything side by side — pick what you need now.
          </p>
        </div>

        <div className="mt-8 overflow-x-auto rounded-xl bg-muted p-2 @[40rem]:p-3">
          <table className="w-full min-w-[36rem] border-separate border-spacing-0 text-left">
            <thead>
              <tr>
                <th className="relative z-0 w-[28%] px-3 py-3 text-xs font-medium tracking-wide text-muted-foreground">
                  Feature
                </th>
                {PLANS.map((plan) => (
                  <th
                    key={plan.key}
                    className={cn(
                      "relative z-0 px-3 py-3 text-center",
                      plan.featured &&
                        "rounded-t-lg bg-primary text-primary-foreground",
                    )}
                  >
                    <div className="relative z-20">
                      <p
                        className={cn(
                          "text-sm font-medium tracking-wide",
                          plan.featured
                            ? "text-primary-foreground"
                            : "text-foreground",
                        )}
                      >
                        {plan.name}
                      </p>
                      <p
                        className={cn(
                          "mt-1 text-lg font-medium tracking-tight",
                          plan.featured
                            ? "text-primary-foreground"
                            : "text-foreground",
                        )}
                      >
                        {plan.price}
                        <span
                          className={cn(
                            "text-xs font-normal",
                            plan.featured
                              ? "text-primary-foreground/70"
                              : "text-muted-foreground",
                          )}
                        >
                          /mo
                        </span>
                      </p>
                    </div>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {FEATURES.map((feature) => (
                <tr key={feature.name}>
                  <td
                    className={cn(
                      "relative z-0 px-3 py-2.5 text-xs tracking-wide text-muted-foreground",
                      rowRule,
                    )}
                  >
                    <span className="relative z-20">{feature.name}</span>
                  </td>
                  {PLANS.map((plan) => (
                    <td
                      key={plan.key}
                      className={cn(
                        "relative z-0 px-3 py-2.5 text-center text-xs tracking-wide",
                        plan.featured
                          ? cn(
                              "bg-primary text-primary-foreground",
                              featuredRowRule,
                            )
                          : cn("text-foreground", rowRule),
                      )}
                    >
                      <span className="relative z-20">{feature[plan.key]}</span>
                    </td>
                  ))}
                </tr>
              ))}
              <tr>
                <td className={cn("relative z-0 px-3 py-3", rowRule)} />
                {PLANS.map((plan) => (
                  <td
                    key={plan.key}
                    className={cn(
                      "relative z-0 px-3 py-3 text-center",
                      plan.featured
                        ? cn(
                            "rounded-b-lg bg-primary text-primary-foreground",
                            featuredRowRule,
                          )
                        : rowRule,
                    )}
                  >
                    <div className="relative z-20 flex justify-center">
                      <Button
                        type="button"
                        size="sm"
                        className="w-full max-w-[9rem]"
                        variant={plan.featured ? "secondary" : "outline"}
                      >
                        {plan.cta}
                      </Button>
                    </div>
                  </td>
                ))}
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add pricing-03
```

### Usage

```tsx
import { Pricing03 } from "@/components/blocks/pricing/pricing-03"
```

```tsx
<Pricing03 />
```
