
Monthly and yearly tabs above three tier columns with one muted highlighted column, badge labels from tier names, checks on features, and sans-serif price type.
@shadcnblocks
block/pricing4/pricing4.tsx
"use client";
import {
Bell,
Briefcase,
Brush,
Building,
CalendarCheck2,
Check,
CheckSquare,
Code,
Database,
FileText,
GitBranch,
GitPullRequest,
LayoutGrid,
MonitorSmartphone,
Rocket,
Settings2,
Users,
Zap,
} from "lucide-react";
import { useState } from "react";
import { cn } from "cn";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
type IconComponent = ElementType<{ className?: string }>;
type Pricing3PlanFeature = | string
| {
text: string;
icon?: IconComponent;
};
interface Pricing3PlansPlan {
name: string;
monthlyPrice: string;
yearlyPrice: string;
period: {
monthly: string;
yearly: string;
};
description: {
monthly: string;
yearly: string;
};
buttonText: string;
buttonUrl?: string;
highlighted?: boolean;
highlightedLabel?: string;
icon?: IconComponent;
image?: string;
features: Pricing3PlanFeature[];
tagline?: string;
bestFor?: string;
planCode?: string;
}
interface Pricing3PlansProps {
heading: string;
description?: string;
plans: Pricing3PlansPlan[];
className?: string;
}
interface Pricing4Props extends Pricing3PlansProps {}
type Props = Partial<Pricing4Props>;
export function pricing3PlanFeatureText(feature: Pricing3PlanFeature): string {
return typeof feature === "string" ? feature : feature.text;
}
export function pricing3PlanFeatureIcon(
feature: Pricing3PlanFeature,
): IconComponent | undefined {
return typeof feature === "string" ? undefined : feature.icon;
}
const defaultProps: Pricing4Props = {
heading: "Simple Pricing Plans",
description:
"Choose the plan that fits your needs. Start free and scale as you grow.",
plans: [
{
icon: Rocket,
image: "https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/pricing-plans/plan1.svg",
name: "Basic Plan",
monthlyPrice: "$0",
yearlyPrice: "$0",
period: {
monthly: "Per month",
yearly: "Per year",
},
description: {
monthly:
"Ideal for individuals getting started. No credit card required.",
yearly:
"Ideal for individuals getting started. No credit card required.",
},
buttonText: "Start for Free",
buttonUrl: "#",
highlighted: false,
planCode: "BASIC",
tagline: "Great for solo developers",
bestFor: "Freelancers just starting out",
features: [
{ icon: Code, text: "Up to 5 components" },
{ icon: LayoutGrid, text: "Community support" },
{ icon: MonitorSmartphone, text: "Weekly updates" },
{ icon: FileText, text: "100MB storage" },
{ icon: GitBranch, text: "Basic analytics" },
],
},
{
icon: Briefcase,
image: "https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/pricing-plans/plan2.svg",
name: "Standard Plan",
monthlyPrice: "$20",
yearlyPrice: "$200",
period: {
monthly: "Per month",
yearly: "Per year",
},
description: {
monthly:
"For growing teams that need more power. Start with a 30-day free trial.",
yearly:
"For growing teams that need more power. Save 16% compared to monthly.",
},
buttonText: "Get Started",
buttonUrl: "#",
highlighted: true,
highlightedLabel: "Most popular",
planCode: "STANDARD",
tagline: "Best for growing teams",
bestFor: "Small dev teams and startups",
features: [
{ icon: Code, text: "Unlimited components" },
{ icon: Brush, text: "Priority support" },
{ icon: Settings2, text: "Daily updates" },
{ icon: CheckSquare, text: "10GB storage" },
{ icon: Zap, text: "Advanced analytics" },
],
},
{
icon: Building,
image: "https://cdn.shadcnblocks.com/shadcnblocks/image-set/modern/pricing-plans/plan3.svg",
name: "Premium Plan",
monthlyPrice: "$80",
yearlyPrice: "$800",
period: {
monthly: "Per month",
yearly: "Per year",
},
description: {
monthly:
"For large organizations with advanced needs. Everything in Standard plus dedicated support.",
yearly:
"For large organizations with advanced needs. Save 16% compared to monthly.",
},
buttonText: "Buy Now",
buttonUrl: "#",
highlighted: false,
planCode: "PREMIUM",
tagline: "Collaborate and scale fast",
bestFor: "Product teams with multiple projects",
features: [
{ icon: Users, text: "Unlimited components" },
{ icon: GitPullRequest, text: "Dedicated support" },
{ icon: CalendarCheck2, text: "Real-time updates" },
{ icon: Bell, text: "Unlimited storage" },
{ icon: Database, text: "Custom integrations" },
],
},
],
};
const Pricing4 = (props: Props) => {
const { heading, description, plans, className } = {
...defaultProps,
...props,
};
const firstHighlightedIndex = plans.findIndex((p) => p.highlighted);
const [isAnnually, setIsAnnually] = useState(false);
return (
<section className={cn("py-32", className)}>
<div className="container mx-auto">
<div className="flex flex-col gap-6">
<h2 className="text-3xl font-medium tracking-tight text-pretty lg:text-5xl">
{heading}
</h2>
<div className="flex flex-col justify-between gap-10 md:flex-row">
{description && (
<p className="max-w-3xl text-muted-foreground lg:text-xl">
{description}
</p>
)}
<Tabs
value={isAnnually ? "annually" : "monthly"}
onValueChange={(value: string) =>
setIsAnnually(value === "annually")
}
className="w-fit shrink-0"
aria-label="Billing period"
>
<TabsList className="grid h-11 w-max grid-cols-2 gap-1 rounded-lg p-1 text-lg">
<TabsTrigger
value="monthly"
className="h-full min-h-0 rounded-md px-7 py-0 font-semibold text-muted-foreground data-active:text-foreground"
>
Monthly
</TabsTrigger>
<TabsTrigger
value="annually"
className="h-full min-h-0 rounded-md px-7 py-0 font-semibold text-muted-foreground data-active:text-foreground"
>
Yearly
</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div className="flex w-full flex-col items-stretch gap-6 md:flex-row">
{plans.map((plan, index) => {
const isHighlighted =
firstHighlightedIndex !== -1 && index === firstHighlightedIndex;
return (
<div
key={plan.name}
className={cn(
"flex w-full flex-col rounded-lg border p-6 text-left",
isHighlighted && "bg-muted",
)}
>
<Badge
variant={isHighlighted ? "default" : "outline"}
className="mb-8 block w-fit uppercase"
>
{plan.name}
</Badge>
<h3 className="text-4xl font-semibold tracking-tight lg:text-5xl">
{isAnnually ? plan.yearlyPrice : plan.monthlyPrice}
</h3>
<p
className={cn(
"text-muted-foreground",
plan.monthlyPrice === "$0" && "invisible",
)}
>
{isAnnually ? plan.period.yearly : plan.period.monthly}
</p>
<Separator className="my-6" />
<div className="flex h-full flex-col justify-between gap-20">
<ul className="space-y-4 text-muted-foreground md:leading-snug">
{plan.features.map((feature, featureIndex) => (
<li
key={featureIndex}
className="flex items-center gap-2"
>
<Check
className="size-4 shrink-0"
aria-hidden="true"
/>
<span>{pricing3PlanFeatureText(feature)}</span>
</li>
))}
</ul>
<Button
className="w-full"
variant={isHighlighted ? "default" : "outline"}
>
{plan.buttonText}
</Button>
</div>
</div>
);
})}
</div>
</div>
</div>
</section>
);
};
export { Pricing4 };




























