Skip to content
All Taste components

Pricing

Pricing, three plans

Three plans side by side with a monthly or yearly switch and the recommended plan picked out.

monthly / yearly · highlighted plan · feature lists · 1 → 3 columns

Simple pricing that grows with you

Start free. Upgrade when you have more clients than invoices left.

Free

For your first few clients

$0$0foreverforever
Start free
  • 5 invoices a month
  • Card and bank payments
  • Email reminders
  • Fernbill branding on invoices

Studio

For small teams

$29$24per monthper month, billed yearly
Talk to us
  • Everything in Pro
  • 5 team seats
  • Client portal
  • Approval before sending
  • Priority support
"use client";
import { useState } from "react";
import { Check } from "lucide-react";
import { cn } from "@/lib/utils";

type Period = "monthly" | "yearly";

const plans = [
  { name: "Free", note: "For your first few clients", price: { monthly: "$0", yearly: "$0" }, cta: "Start free", href: "/signup",
    perks: ["5 invoices a month", "Card and bank payments", "Email reminders", "Fernbill branding on invoices"] },
  { name: "Pro", note: "For full-time freelancers", price: { monthly: "$12", yearly: "$10" }, cta: "Start 14-day trial", href: "/signup?plan=pro", featured: true,
    perks: ["Unlimited invoices", "Recurring invoices", "Your own reminder schedule", "Your logo and colours", "Late fees, added for you"] },
  { name: "Studio", note: "For small teams", price: { monthly: "$29", yearly: "$24" }, cta: "Talk to us", href: "/contact",
    perks: ["Everything in Pro", "5 team seats", "Client portal", "Approval before sending", "Priority support"] },
];

export function PricingThreeTier() {
  const [period, setPeriod] = useState<Period>("monthly");
  return (
    <section aria-labelledby="pricing-title" className="bg-bg font-sans">
      <div className="mx-auto max-w-6xl px-4 py-20 sm:px-6">
        <div className="mx-auto max-w-xl text-center">
          <h2 id="pricing-title" className="text-balance text-3xl font-semibold tracking-tight text-fg sm:text-4xl">Simple pricing that grows with you</h2>
          <p className="mt-4 text-base text-fg-muted sm:text-lg">Start free. Upgrade when you have more clients than invoices left.</p>
        </div>

        <div role="group" aria-label="Billing period" className="mx-auto mt-8 flex w-fit gap-0.5 rounded-lg bg-surface-2 p-[3px]">
          {(["monthly", "yearly"] as const).map((p) => (
            <button
              key={p}
              type="button"
              aria-pressed={period === p}
              onClick={() => setPeriod(p)}
              className="inline-flex h-8 items-center gap-1.5 rounded px-3 text-[13px] font-medium capitalize text-fg-muted transition aria-pressed:bg-bg aria-pressed:text-fg aria-pressed:shadow-sm"
            >
              {p}
              {p === "yearly" && <span className="rounded-full bg-primary-soft px-1.5 text-[11px] text-primary-soft-fg">Save 20%</span>}
            </button>
          ))}
        </div>

        <div className="mt-10 grid gap-4 lg:grid-cols-3">
          {plans.map((plan) => {
            const free = plan.price.monthly === "$0";
            return (
              <article
                key={plan.name}
                aria-label={plan.name + " plan"}
                className={cn(
                  "flex flex-col gap-6 rounded-xl border border-border bg-bg p-6",
                  plan.featured && "border-primary shadow-xl ring-1 ring-primary",
                )}
              >
                <div>
                  <div className="flex items-center justify-between gap-2">
                    <h3 className="text-base font-semibold text-fg">{plan.name}</h3>
                    {plan.featured && <span className="rounded-full bg-primary-soft px-2 py-0.5 text-xs font-medium text-primary-soft-fg">Most popular</span>}
                  </div>
                  <p className="mt-1 text-[13px] text-fg-subtle">{plan.note}</p>
                </div>
                <p className="flex flex-wrap items-baseline gap-x-1.5">
                  <span className="text-4xl font-semibold tracking-tight tabular-nums text-fg">{plan.price[period]}</span>
                  <span className="text-[13px] text-fg-subtle">
                    {free ? "forever" : period === "yearly" ? "per month, billed yearly" : "per month"}
                  </span>
                </p>
                <a
                  href={plan.href}
                  className={cn(
                    "inline-flex h-10 items-center justify-center rounded text-sm font-medium transition",
                    plan.featured ? "bg-primary text-primary-fg hover:bg-primary-hover" : "border border-border-strong text-fg hover:bg-surface",
                  )}
                >
                  {plan.cta}
                </a>
                <ul className="grid gap-2.5 border-t border-border pt-6 text-sm text-fg">
                  {plan.perks.map((perk) => (
                    <li key={perk} className="flex items-start gap-2.5">
                      <Check className="mt-0.5 size-4 shrink-0 text-primary" aria-hidden />
                      {perk}
                    </li>
                  ))}
                </ul>
              </article>
            );
          })}
        </div>
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.