caliper uidocs

Plan comparison

Block

Plans side by side with prices and feature lists.

Installs components/ui/plan-comparison.tsxDepends on @radix-ui/react-slot, lucide-reactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/plan-comparison.json

Simple pricing

Pay per seat. Change plans or cancel at any time.

  • Starter

    For trying things out.

    $0per seat / month

    • 40 components
    • Community support
    • Personal projects
  • Team

    Most popular

    For shipping a product.

    $24per seat / month

    • Every component and block
    • Private repository access
    • Email support
    • Unlimited projects
  • Business

    For admin controls.

    $48per seat / month

    • Everything in Team
    • Priority support
    • Seat management
    • Custom license terms
  • Enterprise

    For custom needs.

    Talk to us

    • Everything in Business
    • Security review
    • Dedicated contact
Prices in USD. Taxes added at checkout.

When to use #

Use it for

  • A pricing page with a few tiers.

Skip it when

  • There is a single plan.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/plan-comparison.json

Adds its 8 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { PlanComparison } from "@/components/ui/plan-comparison"

const plans = [
  { id: "starter", name: "Starter", description: "For trying things out.", price: { monthly: 0, yearly: 0 }, features: ["40 components", "Community support", "Personal projects"], cta: "Start free" },
  { id: "team", name: "Team", description: "For shipping a product.", price: { monthly: 24, yearly: 19 }, features: ["Every component and block", "Private repository access", "Email support", "Unlimited projects"], cta: "Choose Team", highlighted: true, badge: "Most popular" },
  { id: "business", name: "Business", description: "For admin controls.", price: { monthly: 48, yearly: 40 }, features: ["Everything in Team", "Priority support", "Seat management", "Custom license terms"], cta: "Choose Business" },
  { id: "enterprise", name: "Enterprise", description: "For custom needs.", price: "Talk to us", features: ["Everything in Business", "Security review", "Dedicated contact"], cta: "Contact sales" },
];

export function PlanComparisonDemo() {
  const [, setLog] = useState("");
  return (
    <PlanComparison plans={plans} description="Pay per seat. Change plans or cancel at any time." onSelect={(id, billing) => setLog(`Selected ${id} (${billing})`)} footer="Prices in USD. Taxes added at checkout." />
  );
}

API reference #

PlanComparison

component
PropTypeDefault
plans *readonly Plan[]—
titlestring"Simple pricing"
descriptionstring—
currencystring"USD"
onSelect((planId: string, billing: "monthly" | "yearly") => void)—
footerReactNode—
classNamestring—

Accessibility #

It is built on native elements and keeps aria-label and aria-hidden in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.