# Pricing Dock: AI API Pricing

> 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.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/pricing-dock-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/pricing-dock-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/pricing-dock-demo-light-2fe082e20186f133.png
- Registry item: https://keyframeui.com/r/pricing-dock-demo.json
- Categories: pricing, scroll, table
- Listed: 2026-10-15, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/pricing-dock-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/pricing-dock.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/pricing-dock#pricing-dock-demo

## Files

### `registry/demos/pricing-dock-demo.tsx`

```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>
  );
}
```
