Pricing section
Three tiers with a monthly/yearly toggle whose prices roll.
components/blocks/pricing-section.tsx
"use client";
import * as React from "react";
import { Check } from "lucide-react";
import { Button } from "@/components/ui/button";
import { NumberTicker } from "@/components/ui/number-ticker";
import { SegmentedControl } from "@/components/ui/segmented-control";
import { cn } from "@/lib/utils";
const TIERS = [
{ name: "Starter", monthly: 0, blurb: "For side projects and trying things out.", cta: "Start free", features: ["1 project", "10k events / month", "7-day history", "Community support"] },
{ name: "Team", monthly: 29, blurb: "For small teams shipping every week.", cta: "Start 14-day trial", featured: true, features: ["Unlimited projects", "1M events / month", "1-year history", "Alerts and Slack", "Email support"] },
{ name: "Business", monthly: 99, blurb: "For companies with compliance needs.", cta: "Talk to sales", features: ["Everything in Team", "10M events / month", "SSO and audit log", "99.9% uptime SLA", "Dedicated contact"] },
];
export function PricingSection() {
const [cycle, setCycle] = React.useState<"monthly" | "yearly">("yearly");
return (
<section className="w-full rounded-xl border border-border bg-background px-5 py-12 sm:px-10">
<div className="mx-auto max-w-xl text-center">
<h2 className="text-3xl font-semibold tracking-tight sm:text-4xl">Simple pricing that grows with you</h2>
<p className="mt-3 text-muted-foreground">Start free. Upgrade when you need more events, history or seats.</p>
<SegmentedControl
className="mt-6"
aria-label="Billing cycle"
value={cycle}
onValueChange={setCycle}
options={[{ value: "monthly", label: "Monthly" }, { value: "yearly", label: <span>Yearly <span className="text-emerald-500">−20%</span></span> }]}
/>
</div>
<div className="mx-auto mt-10 grid max-w-5xl gap-4 md:grid-cols-3">
{TIERS.map((t) => {
const price = cycle === "yearly" ? Math.round(t.monthly * 0.8) : t.monthly;
return (
<div key={t.name} className={cn("relative flex flex-col rounded-xl border p-6", t.featured ? "border-foreground shadow-xl" : "border-border")}>
{t.featured && <span className="absolute -top-3 left-6 rounded-md bg-primary px-2 py-0.5 text-xs font-medium text-primary-foreground">Most popular</span>}
<h3 className="font-semibold">{t.name}</h3>
<p className="mt-1 text-sm text-muted-foreground">{t.blurb}</p>
<p className="mt-6 flex items-baseline gap-1">
<span className="text-4xl font-semibold tracking-tight"><NumberTicker value={price} format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }} /></span>
<span className="text-sm text-muted-foreground">/ seat / mo</span>
</p>
<p className="h-5 text-xs text-muted-foreground">{t.monthly && cycle === "yearly" ? `Billed $${price * 12} yearly` : ""}</p>
<ul className="mt-6 flex-1 space-y-2.5 text-sm">
{t.features.map((f) => (
<li key={f} className="flex gap-2.5"><Check className="mt-0.5 size-4 shrink-0 text-emerald-500" />{f}</li>
))}
</ul>
<Button className="mt-8 w-full" variant={t.featured ? "primary" : "outline"}>{t.cta}</Button>
</div>
);
})}
</div>
</section>
);
}
export default PricingSection;What's inside
- Rolling prices
- Featured tier
- Monthly / yearly
When to use
- Marketing and pricing pages.
When not to use
- Inside the app: use billing settings.
Installation
$
pnpm dlx shadcn@latest add https://lodeui.com/r/pricing-section.jsonLands in components/blocks/pricing-section.tsx with sample data you replace with your own.
The CLI also installs lucide-react and every Lode component it's built from.
Usage
Import it from @/components/blocks/pricing-section after installing. It ships with sample data, so it renders straight away; swap in your own data when you're ready.
example.tsx
import { PricingSection } from "@/components/blocks/pricing-section";
export default function Page() {
return (
<main className="mx-auto max-w-6xl p-6">
<PricingSection />
</main>
);
}API reference
PricingSection
Three tiers with a monthly / yearly toggle, rolling prices and a featured tier. Also the default export. No props: sample data sits at the top of the file, ready to swap for your own.
No props.
Keyboard interactions
| Keys | Action |
|---|---|
| ←or→ | Switch monthly and yearly |
| Tab | Reach each tier's button |
Accessibility
- The cycle switch is a labelled radio group.
- Prices are read as final amounts.
Motion
- Prices roll between monthly and yearly.
Responsive behavior
- Tiers stack below 768px; the featured tier keeps its border and shadow.
Performance
- Static apart from the price tickers.
Notes for AI
- Edit the tiers array; features are plain strings.
This page as Markdown, for assistants and coding agents:
/blocks/pricing-section/markdownThe full library index for assistants is at /llms.txt.
Lode222 doc pagesllms.txt