An AI API's four plans under a sticky site nav, so the bar docks at `top={56}`. Scroll and each card folds into its column's header. Switch to Yearly from the docked toggle and the docked prices roll; click a docked name to peek at its whole card; hover a column to follow it down.
@keyframeui
registry/demos/pricing-dock-demo.tsx
"use client";
import * as React from "react";
import { PricingDock, type PricingDockGroup, type PricingDockPlan } from "@/components/ui/pricing-dock";
const plans: PricingDockPlan[] = [
{
id: "free",
name: "Free",
price: 0,
caption: "No card needed",
pitch: "Prototype with every fast model.",
highlights: ["20 requests per minute", "1M tokens a month", "Fast and embedding models", "Community support"],
cta: { label: "Start building", href: "#" },
},
{
id: "pro",
name: "Pro",
price: { monthly: 40, yearly: 32 },
caption: { monthly: "Billed monthly", yearly: "Billed $384 yearly" },
pitch: "For apps with real users in production.",
highlights: ["500 requests per minute", "Reasoning models", "Batch API at half price", "5 team seats"],
featured: true,
badge: "Most popular",
cta: { label: "Start free trial", href: "#" },
},
{
id: "scale",
name: "Scale",
price: { monthly: 400, yearly: 320 },
caption: { monthly: "Billed monthly", yearly: "Billed $3,840 yearly" },
pitch: "High volume, tuned to your own data.",
highlights: ["5,000 requests per minute", "Fine-tuning and evals", "Zero data retention", "Priority support"],
cta: { label: "Upgrade to Scale", href: "#" },
},
{
id: "enterprise",
name: "Enterprise",
price: "Custom",
caption: "Annual contract",
pitch: "Private deployments and custom terms.",
highlights: ["Private model deployments", "SSO, SCIM and audit logs", "Pin data to a region", "99.99% uptime SLA"],
cta: { label: "Contact sales", href: "#" },
},
];
const groups: PricingDockGroup[] = [
{
label: "Usage",
rows: [
{
feature: "Requests per minute",
hint: "Sustained rate across all models. Short bursts up to 2× are allowed.",
values: { free: "20", pro: "500", scale: "5,000", enterprise: "Custom" },
},
{ feature: "Tokens per month", values: { free: "1M", pro: "50M", scale: "1B", enterprise: "Unlimited" } },
{
feature: "Context window",
hint: "The most tokens a single request can read, prompt and files included.",
values: { free: "32K", pro: "200K", scale: "1M", enterprise: "1M" },
},
{ feature: "Batch API", values: { free: false, pro: true, scale: true, enterprise: true } },
{ feature: "Prompt caching", values: { free: false, pro: true, scale: true, enterprise: true } },
],
},
{
label: "Models",
rows: [
{ feature: "Fast models", values: { free: true, pro: true, scale: true, enterprise: true } },
{ feature: "Reasoning models", values: { free: false, pro: true, scale: true, enterprise: true } },
{ feature: "Embeddings and rerank", values: { free: true, pro: true, scale: true, enterprise: true } },
{
feature: "Fine-tuning",
hint: "Train on your own examples and serve the result at the base model's price.",
values: { free: false, pro: false, scale: true, enterprise: true },
},
{ feature: "Private deployments", values: { free: false, pro: false, scale: false, enterprise: true } },
],
},
{
label: "Collaboration",
rows: [
{ feature: "Team seats", values: { free: "1", pro: "5", scale: "25", enterprise: "Unlimited" } },
{ feature: "Shared projects", values: { free: false, pro: true, scale: true, enterprise: true } },
{ feature: "Usage alerts", values: { free: true, pro: true, scale: true, enterprise: true } },
{ feature: "Role-based access", values: { free: false, pro: false, scale: true, enterprise: true } },
],
},
{
label: "Security",
rows: [
{ feature: "SSO (SAML, OIDC)", values: { free: false, pro: false, scale: "Add-on", enterprise: true } },
{
feature: "Data retention",
hint: "How long prompts and outputs are kept for abuse monitoring. Never used for training.",
values: { free: "30 days", pro: "30 days", scale: "Zero", enterprise: "Custom" },
},
{ feature: "SOC 2 Type II report", values: { free: false, pro: true, scale: true, enterprise: true } },
{ feature: "Audit logs", values: { free: false, pro: false, scale: true, enterprise: true } },
{ feature: "Region pinning", values: { free: false, pro: false, scale: "US or EU", enterprise: "Any region" } },
],
},
{
label: "Support",
rows: [
{ feature: "Support", values: { free: "Community", pro: "Email", scale: "Priority, 4h", enterprise: "Dedicated" } },
{ feature: "Uptime SLA", values: { free: false, pro: false, scale: "99.9%", enterprise: "99.99%" } },
],
},
];
// The page scrolls inside a box so the demo fits the docs. On a real page,
// drop the box and `containerRef`, and set `top` to your nav's height.
export function PricingDockDemo() {
const scrollRef = React.useRef<HTMLDivElement>(null);
return (
<div className="relative isolate w-full overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
<div ref={scrollRef} className="h-[var(--demo-height,44rem)] overflow-y-auto overscroll-contain">
<nav className="sticky top-0 z-30 flex h-14 items-center gap-6 border-b border-zinc-900/[0.06] bg-white/80 px-5 backdrop-blur-md dark:border-white/[0.06] dark:bg-zinc-950/80">
<span className="flex items-center gap-2 text-sm font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">
<svg viewBox="0 0 20 20" aria-hidden className="size-5">
<rect width="20" height="20" rx="5" className="fill-zinc-900 dark:fill-white" />
<path d="M5 14a5 5 0 0 1 10 0" fill="none" strokeWidth="2.25" strokeLinecap="round" className="stroke-violet-500" />
</svg>
Arclight
</span>
<span className="hidden gap-5 text-sm text-zinc-500 sm:flex">
<a href="#">Models</a>
<a href="#">Docs</a>
<a href="#" className="text-zinc-900 dark:text-zinc-50">
Pricing
</a>
</span>
<a
href="#"
className="ml-auto rounded-full bg-zinc-900 px-3.5 py-1.5 text-sm font-medium text-white dark:bg-white dark:text-zinc-900"
>
Get an API key
</a>
</nav>
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<header className="max-w-xl pt-14 pb-10 sm:pt-20">
<p className="font-mono text-[11px] tracking-widest text-violet-600 uppercase dark:text-violet-400">
Pricing
</p>
<h2 className="mt-3 text-3xl/tight font-semibold tracking-tight text-balance text-zinc-900 sm:text-4xl/tight dark:text-zinc-50">
Start free. Pay as your traffic grows.
</h2>
<p className="mt-3 text-sm/6 text-pretty text-zinc-500">
Every plan gets the same models and the same speed. Higher tiers raise your limits and add the controls
a security review asks for.
</p>
</header>
<PricingDock
containerRef={scrollRef}
top={56}
plans={plans}
groups={groups}
billing={[
{ id: "monthly", label: "Monthly" },
{ id: "yearly", label: "Yearly", note: "−20%" },
]}
intro="Every plan includes the playground, SDKs in nine languages and live usage dashboards."
className="[--dock-accent:var(--color-violet-600)] [--dock-accent-foreground:white] [--dock-surface:white] dark:[--dock-accent:var(--color-violet-500)] dark:[--dock-surface:var(--color-zinc-900)]"
/>
<footer className="flex flex-col items-center py-24 text-center">
<h3 className="text-xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">
Need more than 5,000 requests a minute?
</h3>
<p className="mt-2 max-w-sm text-sm/6 text-pretty text-zinc-500">
We set custom limits within a day and can run models in your own cloud.
</p>
<a
href="#"
className="mt-5 rounded-full bg-violet-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-violet-500"
>
Talk to sales
</a>
</footer>
</div>
</div>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/pricing-dock#pricing-dock-demo




























