Choose the perfect plan for your team
Compare features across all plans and find the best fit for your needs.
Basic
Perfect for small teams
$29/month
Popular
Pro
For growing businesses
$99/month
Enterprise
For large organizations
Custom
Team members
Number of users included
Basic:
5 usersPro:
25 usersEnterprise:
UnlimitedStorage
Total storage capacity
Basic:
10 GBPro:
100 GBEnterprise:
UnlimitedAPI access
Programmatic access to platform
Basic:
Pro:
Enterprise:
Advanced analytics
Detailed insights and reporting
Basic:
Pro:
Enterprise:
Custom integrations
Build your own integrations
Basic:
Pro:
Enterprise:
Priority support
24/7 dedicated support team
Basic:
Pro:
EmailEnterprise:
Phone & EmailSLA guarantee
Uptime service level agreement
Basic:
Pro:
99.9%Enterprise:
99.99%SSO & SAML
Single sign-on authentication
Basic:
Pro:
Enterprise:
Audit logs
Complete activity tracking
Basic:
Pro:
30 daysEnterprise:
UnlimitedCustom contracts
Flexible terms and pricing
Basic:
Pro:
Enterprise:
Features
- ✅ Side-by-side comparison - Clear plan comparison layout
- ✅ Plan cards - Beautiful plan headers with pricing
- ✅ Flexible values - Boolean, string, or custom React nodes
- ✅ Highlighted plans - Mark popular or recommended plans
- ✅ Icon support - Add icons to plan cards
- ✅ Feature descriptions - Optional descriptions for each feature
- ✅ Responsive design - Mobile-friendly with plan selector
- ✅ Hover effects - Smooth row highlighting
- ✅ TypeScript support - Full type safety
- ✅ Customizable - Easy to style and extend
Installation
Copy and paste the following code into your project.
import { Check, X, LucideIcon } from "lucide-react";
import { ReactNode } from "react";
interface ComparisonFeature {
name: string;
description?: string;
basic: boolean | string | ReactNode;
pro: boolean | string | ReactNode;
enterprise: boolean | string | ReactNode;
}
interface PricingComparisonProps {
headline?: string;
description?: string;
plans: {
basic: {
name: string;
price: string;
period?: string;
description?: string;
icon?: LucideIcon;
highlighted?: boolean;
};
pro: {
name: string;
price: string;
period?: string;
description?: string;
icon?: LucideIcon;
highlighted?: boolean;
};
enterprise: {
name: string;
price: string;
period?: string;
description?: string;
icon?: LucideIcon;
highlighted?: boolean;
};
};
features: ComparisonFeature[];
}
export function PricingComparison({
headline = "Compare plans",
description,
plans,
features,
}: PricingComparisonProps) {
const renderValue = (value: boolean | string | ReactNode) => {
if (typeof value === "boolean") {
return value ? (
<Check className="h-5 w-5 text-emerald-400" />
) : (
<X className="h-5 w-5 text-zinc-600" />
);
}
return <span className="text-sm font-medium text-white">{value}</span>;
};
const PlanCard = ({
plan,
type,
}: {
plan: (typeof plans)[keyof typeof plans];
type: string;
}) => {
const Icon = plan.icon;
return (
<div
className={`relative rounded-2xl border p-6 transition-all ${
plan.highlighted
? "border-blue-500/50 bg-gradient-to-b from-blue-500/10 to-transparent"
: "border-zinc-800/50 bg-zinc-950"
}`}
>
{plan.highlighted && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
<span className="rounded-full bg-blue-500 px-3 py-1 text-xs font-semibold text-white">
Popular
</span>
</div>
)}
<div className="text-center">
{Icon && (
<div className="mb-4 flex justify-center">
<div className="rounded-lg bg-blue-500/10 p-3">
<Icon className="h-6 w-6 text-blue-400" />
</div>
</div>
)}
<h3 className="text-xl font-bold text-white">{plan.name}</h3>
{plan.description && (
<p className="mt-2 text-sm text-zinc-500">{plan.description}</p>
)}
<div className="mt-4">
<span className="text-4xl font-bold text-white">{plan.price}</span>
{plan.period && (
<span className="ml-2 text-sm text-zinc-500">{plan.period}</span>
)}
</div>
</div>
</div>
);
};
return (
<section className="w-full bg-black py-12 sm:py-16 lg:py-20 z-30">
<div className="container px-4 md:px-6">
<div className="mx-auto max-w-3xl space-y-4 text-center">
<h2 className="text-3xl font-bold tracking-tighter text-white sm:text-4xl md:text-5xl">
{headline}
</h2>
{description && (
<p className="text-lg text-zinc-400 md:text-xl">{description}</p>
)}
</div>
<div className="mx-auto mt-12 max-w-7xl">
<div className="hidden lg:grid lg:grid-cols-4 lg:gap-6">
<div className="p-6" />
<PlanCard plan={plans.basic} type="basic" />
<PlanCard plan={plans.pro} type="pro" />
<PlanCard plan={plans.enterprise} type="enterprise" />
</div>
<div className="mt-8 overflow-hidden rounded-2xl border border-zinc-800/50 bg-zinc-950">
<div className="border-b border-zinc-800/50 p-4 lg:hidden">
<select className="w-full rounded-lg border border-zinc-800 bg-zinc-900 px-4 py-2 text-white">
<option>{plans.basic.name}</option>
<option>{plans.pro.name}</option>
<option>{plans.enterprise.name}</option>
</select>
</div>
<div className="divide-y divide-zinc-800/50">
{features.map((feature, index) => (
<div
key={index}
className="grid grid-cols-1 gap-4 p-6 transition-colors hover:bg-zinc-900/30 lg:grid-cols-4 lg:items-center"
>
<div>
<h4 className="font-semibold text-white">{feature.name}</h4>
{feature.description && (
<p className="mt-1 text-sm text-zinc-500">
{feature.description}
</p>
)}
</div>
<div className="flex items-center justify-center lg:justify-center">
<div className="lg:hidden mr-auto text-sm text-zinc-500">
{plans.basic.name}:
</div>
{renderValue(feature.basic)}
</div>
<div className="flex items-center justify-center lg:justify-center">
<div className="lg:hidden mr-auto text-sm text-zinc-500">
{plans.pro.name}:
</div>
{renderValue(feature.pro)}
</div>
<div className="flex items-center justify-center lg:justify-center">
<div className="lg:hidden mr-auto text-sm text-zinc-500">
{plans.enterprise.name}:
</div>
{renderValue(feature.enterprise)}
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
);
}Usage
Basic Usage
import PricingComparison from "@/components/ui/pricing-comparison";
import { Zap, Rocket, Building2 } from "lucide-react";
export default function Page() {
return (
<PricingComparison
headline="Compare plans"
description="Find the perfect plan for your needs."
plans={{
basic: {
name: "Basic",
price: "$29",
period: "/month",
icon: Zap,
},
pro: {
name: "Pro",
price: "$99",
period: "/month",
icon: Rocket,
highlighted: true,
},
enterprise: {
name: "Enterprise",
price: "Custom",
icon: Building2,
},
}}
features={[
{
name: "Team members",
basic: "5 users",
pro: "25 users",
enterprise: "Unlimited",
},
{
name: "API access",
basic: false,
pro: true,
enterprise: true,
},
{
name: "Priority support",
basic: false,
pro: "Email",
enterprise: "Phone & Email",
},
]}
/>
);
}With Descriptions
<PricingComparison
headline="Choose your plan"
plans={{
basic: {
name: "Starter",
price: "$19",
period: "/month",
description: "Perfect for individuals",
},
pro: {
name: "Professional",
price: "$49",
period: "/month",
description: "For growing teams",
highlighted: true,
},
enterprise: {
name: "Enterprise",
price: "Custom",
description: "For large organizations",
},
}}
features={[
{
name: "Storage",
description: "Total storage capacity included",
basic: "10 GB",
pro: "100 GB",
enterprise: "Unlimited",
},
{
name: "Advanced analytics",
description: "Detailed insights and reporting",
basic: false,
pro: true,
enterprise: true,
},
]}
/>Custom Values
<PricingComparison
plans={{
basic: { name: "Basic", price: "$29" },
pro: { name: "Pro", price: "$99" },
enterprise: { name: "Enterprise", price: "Custom" },
}}
features={[
{
name: "Support",
basic: false,
pro: <span className="text-yellow-400">Email only</span>,
enterprise: (
<div className="flex items-center gap-2">
<span className="text-emerald-400">24/7 Phone</span>
</div>
),
},
]}
/>Props
PricingComparisonProps
| Prop | Type | Default | Description |
|---|---|---|---|
headline | string | "Compare plans" | Section headline |
description | string | undefined | Optional description |
plans | Plans | Required | Plan configuration object |
features | ComparisonFeature[] | Required | Array of features to compare |
Plan Object
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | Required | Plan name |
price | string | Required | Plan price |
period | string | undefined | Billing period (e.g., "/month") |
description | string | undefined | Plan description |
icon | LucideIcon | undefined | Plan icon |
highlighted | boolean | false | Highlight as popular plan |
ComparisonFeature
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | Required | Feature name |
description | string | undefined | Feature description |
basic | boolean | string | ReactNode | Required | Value for basic plan |
pro | boolean | string | ReactNode | Required | Value for pro plan |
enterprise | boolean | string | ReactNode | Required | Value for enterprise plan |
TypeScript Interface
interface PricingComparisonProps {
headline?: string;
description?: string;
plans: {
basic: PlanConfig;
pro: PlanConfig;
enterprise: PlanConfig;
};
features: ComparisonFeature[];
}
interface PlanConfig {
name: string;
price: string;
period?: string;
description?: string;
icon?: LucideIcon;
highlighted?: boolean;
}
interface ComparisonFeature {
name: string;
description?: string;
basic: boolean | string | ReactNode;
pro: boolean | string | ReactNode;
enterprise: boolean | string | ReactNode;
}Value Types
Features support three value types:
- Boolean: Renders checkmark (✓) or X (✗)
- String: Displays as text (e.g., "10 GB", "Unlimited")
- ReactNode: Custom JSX for complex values
Responsive Behavior
- Desktop (lg+): Side-by-side comparison with plan cards
- Mobile: Stacked layout with plan selector dropdown
Use Cases
Perfect for:
- Pricing page comparisons
- Feature tier displays
- Product plan differences
- Service level comparisons
- Subscription options
- Package comparisons
- Membership tiers
- License comparisons