Pricing

Pricing layouts for plans, billing, and feature comparisons.

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.

Simple pricing

Pick a plan and ship. Upgrade when the team grows.

  • Hobby

    $0/mo

    For side projects and trying the kit.

    • 3 projects
    • Basic blocks
    • Community support
  • Pro

    Popular

    $29/mo

    For teams shipping real product surfaces.

    • Unlimited projects
    • All blocks
    • Priority support
    • Team seats
  • Enterprise

    Custom

    Security, SLAs, and dedicated onboarding.

    • SSO & audit logs
    • Custom contracts
    • Dedicated support
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

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

Usage

import { Pricing01 } from "@/components/blocks/pricing/pricing-01"
<Pricing01 />

Billing toggle

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

Pay monthly or yearly

Yearly billing saves about 20% on every plan.

  • Starter

    $/mo

    Billed yearly

    Solo builders and early launches.

    • 10 projects
    • Core blocks
    • Email support
  • Team

    $/mo

    Billed yearly

    Growing product and design teams.

    • Unlimited projects
    • All blocks
    • Shared library
    • Slack support
  • Business

    $/mo

    Billed yearly

    Orgs that need control and scale.

    • Everything in Team
    • SSO
    • Audit log
    • Priority SLA
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

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

Usage

import { Pricing02 } from "@/components/blocks/pricing/pricing-02"
<Pricing02 />

Comparison table

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

Compare plans

Everything side by side — pick what you need now.

Feature

Hobby

$0/mo

Pro

$29/mo

Business

$99/mo

Projects3UnlimitedUnlimited
BlocksCoreAllAll + private
Seats110Unlimited
SupportCommunityPriorityDedicated
SSO——Included
Audit log——Included
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" },  { key: "pro", name: "Pro", price: "$29", cta: "Get Pro", featured: true },  { key: "business", name: "Business", price: "$99", cta: "Get Business" },] 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

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

Usage

import { Pricing03 } from "@/components/blocks/pricing/pricing-03"
<Pricing03 />