Sections
Get Started
UI
Components
- Animated Beam
- Animated List
- Animated Logo
- Animated Theme Toggler
- Avatar Circles
- Bento Grid
- Big Name Text
- Border Beam
- Dock
- Feature Card
- Filter Dropdown
- Footer Link
- Hero Badge
- Hero Buttons
- Hyper Text
- Lens
- Magic Card
- Marquee
- Meteors
- Onboarding Form
- Pricing Card
- Scroll Text
- Section Header
- Site Header
- Status Card
- Status
- Tweet Card
Simple, transparent pricing
Choose your perfect plan
Start free and scale as you grow. No hidden fees, cancel anytime.
MonthlyYearlySave 20%
Starter
Perfect for individuals and small projects
$0
- Up to 3 projects
- Basic analytics
- 24-hour support response
- 1GB storage
- Community access
Most Popular
Pro
Best for growing teams and businesses
$29/month
- Unlimited projects
- Advanced analytics & insights
- Priority support (4-hour response)
- 100GB storage
- Team collaboration
- Custom integrations
- API access
Enterprise
For organizations that need more
$99/month
- Everything in Pro
- Unlimited storage
- Dedicated account manager
- Custom SLA
- SSO & SAML
- Advanced security
- On-premise deployment
- White-label options
Trusted by 10,000+ teams worldwide • 30-day money-back guarantee • No credit card required
"use client";
import { motion, useScroll, useTransform, useInView } from "framer-motion";
import { useRef, useState } from "react";
import { Check, Sparkles, Zap, Crown, ArrowRight } from "lucide-react";
import Link from "next/link";
import { Button } from "@/registry/sase/button";
const plans = [
{
name: "Starter",
description: "Perfect for individuals and small projects",
price: { monthly: 0, yearly: 0 },
features: [
"Up to 3 projects",
"Basic analytics",
"24-hour support response",
"1GB storage",
"Community access",
],
cta: "Get Started Free",
popular: false,
icon: Zap,
gradient: "from-slate-500 to-zinc-600",
accentColor: "rgb(148, 163, 184)",
},
{
name: "Pro",
description: "Best for growing teams and businesses",
price: { monthly: 29, yearly: 290 },
features: [
"Unlimited projects",
"Advanced analytics & insights",
"Priority support (4-hour response)",
"100GB storage",
"Team collaboration",
"Custom integrations",
"API access",
],
cta: "Start Free Trial",
popular: true,
icon: Sparkles,
gradient: "from-violet-500 via-purple-500 to-fuchsia-500",
accentColor: "rgb(139, 92, 246)",
},
{
name: "Enterprise",
description: "For organizations that need more",
price: { monthly: 99, yearly: 990 },
features: [
"Everything in Pro",
"Unlimited storage",
"Dedicated account manager",
"Custom SLA",
"SSO & SAML",
"Advanced security",
"On-premise deployment",
"White-label options",
],
cta: "Contact Sales",
popular: false,
icon: Crown,
gradient: "from-amber-500 to-orange-500",
accentColor: "rgb(245, 158, 11)",
},
];
const smoothEase = [0.4, 0, 0.2, 1] as const;
const gentleEase = [0.25, 0.46, 0.45, 0.94] as const;
function PricingCard({
plan,
index,
isYearly,
}: {
plan: (typeof plans)[number];
index: number;
isYearly: boolean;
}) {
const cardRef = useRef<HTMLDivElement>(null);
const isInView = useInView(cardRef, { once: true, margin: "-100px" });
const [isHovered, setIsHovered] = useState(false);
const price = isYearly ? plan.price.yearly : plan.price.monthly;
const Icon = plan.icon;
return (
<motion.div
ref={cardRef}
initial={{ opacity: 0, y: 60 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{
duration: 0.7,
delay: index * 0.15,
ease: gentleEase,
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`group relative h-full ${plan.popular ? "z-10" : ""}`}
>
{/* Popular plan - subtle elevation */}
{plan.popular && (
<motion.div
className="absolute -inset-px rounded-3xl bg-linear-to-br from-violet-500/30 via-purple-500/20 to-fuchsia-500/30"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 0.3 }}
style={{ filter: "blur(1px)" }}
/>
)}
<motion.div
className={`relative flex h-full flex-col overflow-hidden rounded-3xl border backdrop-blur-sm transition-all duration-500 ${plan.popular ? "border-violet-500/30 bg-zinc-900/90 shadow-xl shadow-violet-500/10" : "border-white/10 bg-zinc-900/60 hover:border-white/20 hover:shadow-lg hover:shadow-violet-500/5"}`}
animate={{
y: plan.popular ? (isHovered ? -8 : -4) : isHovered ? -4 : 0,
scale: plan.popular ? 1.02 : 1,
}}
transition={{ duration: 0.4, ease: smoothEase }}
>
{/* Popular badge */}
{plan.popular && (
<div className="absolute right-4 top-4">
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.5, duration: 0.4 }}
className="flex items-center gap-1.5 rounded-full bg-linear-to-r from-violet-500/20 to-purple-500/20 px-3 py-1.5 text-xs font-medium text-violet-300 ring-1 ring-violet-500/30"
>
<Sparkles className="h-3 w-3" />
Most Popular
</motion.div>
</div>
)}
{/* Card content */}
<div className="flex flex-1 flex-col p-8">
{/* Icon */}
<motion.div
className={`mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-linear-to-br ${plan.gradient}`}
animate={{
scale: isHovered ? 1.1 : 1,
rotate: isHovered ? 5 : 0,
}}
transition={{ duration: 0.3, ease: smoothEase }}
>
<Icon className="h-6 w-6 text-white" />
</motion.div>
{/* Plan name & description */}
<h3 className="text-xl font-bold text-white">{plan.name}</h3>
<p className="mt-2 text-sm text-white/60">{plan.description}</p>
{/* Price */}
<div className="mt-6 flex items-baseline gap-1">
<motion.span
key={price}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className="text-4xl font-bold tracking-tight text-white"
>
${price}
</motion.span>
{price > 0 && (
<span className="text-sm text-white/50">
/{isYearly ? "year" : "month"}
</span>
)}
</div>
{/* Yearly savings badge */}
{isYearly && price > 0 && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 }}
className="mt-2 inline-flex w-fit items-center gap-1 rounded-full bg-emerald-500/10 px-2 py-1 text-xs font-medium text-emerald-400"
>
Save ${plan.price.monthly * 12 - plan.price.yearly}
</motion.div>
)}
{/* Divider */}
<div className="my-6 h-px bg-linear-to-r from-transparent via-white/10 to-transparent" />
{/* Features */}
<ul className="flex-1 space-y-3">
{plan.features.map((feature, i) => (
<motion.li
key={feature}
initial={{ opacity: 0, x: -10 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{
duration: 0.4,
delay: index * 0.15 + i * 0.05,
ease: gentleEase,
}}
className="flex items-start gap-3 text-sm text-white/70"
>
<motion.div
className={`mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-linear-to-br ${plan.gradient}`}
style={{ opacity: 0.8 }}
>
<Check className="h-3 w-3 text-white" />
</motion.div>
{feature}
</motion.li>
))}
</ul>
{/* CTA Button */}
<motion.div
className="mt-8"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{plan.popular ? (
<div className="group/btn relative">
{/* Glow effect */}
<motion.div className="absolute -inset-1 rounded-xl bg-linear-to-r from-violet-500 via-purple-500 to-fuchsia-500 opacity-0 blur-lg transition-opacity duration-300 group-hover/btn:opacity-50" />
<Button
asChild
className="relative h-12 w-full overflow-hidden rounded-xl bg-linear-to-r from-violet-500 to-purple-600 text-sm font-semibold text-white shadow-lg shadow-purple-500/25 transition-all hover:shadow-purple-500/40"
>
<Link
href="#get-started"
className="flex items-center justify-center gap-2"
>
{plan.cta}
<motion.span
animate={{ x: [0, 3, 0] }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: "easeInOut",
}}
>
<ArrowRight className="h-4 w-4" />
</motion.span>
</Link>
</Button>
</div>
) : (
<Button
asChild
variant="outline"
className="h-12 w-full rounded-xl border-white/20 bg-white/5 text-sm font-semibold text-white transition-all hover:border-white/30 hover:bg-white/10"
>
<Link
href="#get-started"
className="flex items-center justify-center gap-2"
>
{plan.cta}
<ArrowRight className="h-4 w-4 opacity-50 transition-opacity group-hover:opacity-100" />
</Link>
</Button>
)}
</motion.div>
</div>
{/* Subtle hover gradient overlay */}
<motion.div
className={`pointer-events-none absolute inset-0 bg-linear-to-br ${plan.gradient} opacity-0 transition-opacity duration-500`}
animate={{ opacity: isHovered ? 0.03 : 0 }}
/>
</motion.div>
</motion.div>
);
}
export function PricingCardDemo() {
const sectionRef = useRef<HTMLElement>(null);
const [isYearly, setIsYearly] = useState(false);
const isInView = useInView(sectionRef, { once: true, margin: "-100px" });
const { scrollYProgress } = useScroll({
target: sectionRef,
offset: ["start end", "end start"],
});
const backgroundY = useTransform(scrollYProgress, [0, 1], ["0%", "30%"]);
return (
<section
ref={sectionRef}
className="relative w-full overflow-hidden py-24 sm:py-32 md:py-40"
>
{/* Background elements */}
<motion.div
className="pointer-events-none absolute inset-0 -z-10"
style={{ y: backgroundY }}
>
{/* Ambient glow */}
<div
className="absolute left-1/2 top-1/4 h-[600px] w-[600px] -translate-x-1/2 rounded-full blur-[150px]"
style={{
background:
"radial-gradient(circle, rgba(139, 92, 246, 0.08) 0%, transparent 70%)",
}}
/>
<div
className="absolute right-0 top-1/2 h-[400px] w-[400px] rounded-full blur-[120px]"
style={{
background:
"radial-gradient(circle, rgba(168, 85, 247, 0.05) 0%, transparent 70%)",
}}
/>
</motion.div>
<div className="container mx-auto px-4">
{/* Section header */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, ease: gentleEase }}
className="mx-auto mb-16 max-w-2xl text-center sm:mb-20"
>
{/* Badge */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={isInView ? { opacity: 1, scale: 1 } : {}}
transition={{ duration: 0.5, delay: 0.1 }}
className="mb-6 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-2 text-sm font-medium text-white/80 backdrop-blur-sm"
>
<Sparkles className="h-4 w-4 text-violet-400" />
Simple, transparent pricing
</motion.div>
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-5xl">
Choose your{" "}
<span className="bg-linear-to-r from-violet-400 via-purple-400 to-fuchsia-400 bg-clip-text text-transparent">
perfect plan
</span>
</h2>
<p className="mt-4 text-base text-white/60 sm:text-lg">
Start free and scale as you grow. No hidden fees, cancel anytime.
</p>
{/* Billing toggle */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.2 }}
className="mt-8 flex items-center justify-center gap-4"
>
<span
className={`text-sm transition-colors ${!isYearly ? "text-white" : "text-white/50"}`}
>
Monthly
</span>
<button
onClick={() => setIsYearly(!isYearly)}
className="relative h-7 w-14 rounded-full bg-white/10 p-1 transition-colors hover:bg-white/15"
>
<motion.div
className="h-5 w-5 rounded-full bg-linear-to-br from-violet-500 to-purple-600 shadow-lg"
animate={{ x: isYearly ? 26 : 0 }}
transition={{ duration: 0.3, ease: smoothEase }}
/>
</button>
<span
className={`flex items-center gap-1.5 text-sm transition-colors ${isYearly ? "text-white" : "text-white/50"}`}
>
Yearly
<span className="rounded-full bg-emerald-500/10 px-2 py-0.5 text-xs font-medium text-emerald-400">
Save 20%
</span>
</span>
</motion.div>
</motion.div>
{/* Pricing cards - column layout */}
<div className="mx-auto flex max-w-md flex-col gap-6">
{plans.map((plan, index) => (
<PricingCard
key={plan.name}
plan={plan}
index={index}
isYearly={isYearly}
/>
))}
</div>
{/* Trust indicators */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.6, ease: gentleEase }}
className="mx-auto mt-16 max-w-2xl text-center sm:mt-20"
>
<p className="text-sm text-white/40">
Trusted by 10,000+ teams worldwide • 30-day money-back guarantee •
No credit card required
</p>
</motion.div>
</div>
</section>
);
}
Installation
pnpm dlx shadcn@latest add https://sase-ui.vercel.app/r/pricing-card.json
Usage
import { PricingCard } from "@/components/ui/pricing-card";
import { Sparkles } from "lucide-react";<PricingCard
name="Pro"
description="Best for growing teams"
price={{ monthly: 29, yearly: 290 }}
features={["Unlimited projects", "Advanced analytics", "Priority support"]}
cta="Start Free Trial"
popular={true}
icon={Sparkles}
gradient="from-violet-500 via-purple-500 to-fuchsia-500"
isYearly={false}
/>