A customizable pricing card component with smooth CSS-based hover animations.
Supports plan labels, badges, prices, descriptions, and call-to-action buttons, making it ideal for subscription tiers, product offers, or feature showcases.
Serious opportunities for serious investors.\nThe choice is yours.
Billed as $299 yearly
npm i clsx tailwind-merge framer-motionlib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}PricingCard.tsx
"use client";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";
interface CardProps {
planType?: string;
badgeText?: string;
price: string | number;
duration?: string;
description?: string;
btnText?: string;
bottomText?: string;
className?: string;
onClick?: () => void;
animate?: boolean;
}
export default function PricingCard({
planType,
badgeText,
price,
duration,
description,
btnText,
bottomText,
onClick,
className,
animate = true, // default to true
}: CardProps) {
const Wrapper = animate ? motion.div : "div";
return (
<Wrapper
{...(animate
? {
initial: { scale: 0.9 },
whileHover: { scale: 1 },
transition: { duration: 0.3 },
}
: {})}
className={cn(
`max-w-3xs w-full max-h-fit text-white rounded-2xl [background:radial-gradient(circle_at_top,_#172554,#020617)] relative`,
className
)}
>
<div className="w-full h-full rounded-2xl border-l-[#312e81] border-l-2 border-r-[#312e81] border-r-2 relative hover:[box-shadow:inset_5px_5px_10px_rgba(59,_130,_246,_0.3),inset_-5px_-5px_10px_rgba(59,_130,_246,_0.3)] flex flex-col items-center justify-center gap-1 p-4 sm:p-0">
{/* header */}
<div className="flex items-baseline justify-center gap-3 sm:gap-4 flex-wrap ">
{planType && (
<h4 className="text-xs sm:text-sm font-medium text-zinc-400 tracking-wider ">
{planType}
</h4>
)}
{badgeText && (
<div className="p-[2px] rounded-3xl bg-[radial-gradient(circle_at_left,#4b5563_0%,#09090b_100%)] w-fit">
<div className="px-5 py-2 rounded-3xl [background:radial-gradient(circle_at_right,#4b5563_0%,#09090b_100%)] text-[10px] sm:text-xs text-zinc-300 leading-none">
{badgeText}
</div>
</div>
)}
</div>
{/* mid */}
<div className="flex flex-col items-center justify-center ">
{/* price */}
<div className="flex items-center justify-center relative ">
<div className="absolute top-2 -left-7 ">
<svg
xmlns="http://www.w3.org/2000/svg"
width="28"
height="28"
viewBox="0 0 24 24"
fill="none"
stroke="#a1a1aa"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-dollar-sign-icon lucide-dollar-sign"
>
<line x1="12" x2="12" y1="2" y2="22" />
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
</svg>
</div>
<div className="text-6xl sm:text-8xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-[#f3f4f6] to-[#52525b]">
{price}
</div>
</div>
<h2 className="text-[10px] sm:text-xs text-zinc-400 font-semibold tracking-wider">
{duration}
</h2>
<p className="text-center text-balance text-zinc-400 text-xs sm:text-sm ">
{description}
</p>
</div>
{/* Bottom */}
<div className="flex flex-col items-center justify-center">
<button
className="bg-zinc-200 rounded-xl px-8 sm:px-12 py-2 text-xs text-zinc-800 flex items-center justify-center font-semibold gap-2 max-w-fit shadow-sm shadow-zinc-400"
onClick={onClick}
>
{btnText}
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-move-right-icon lucide-move-right"
>
<path d="M18 8L22 12L18 16" />
<path d="M2 12H22" />
</svg>
</button>
<p className="text-center text-balance text-zinc-400 text-[10px] sm:text-xs px-2 mt-2 tracking-wide">
{bottomText}
</p>
</div>
</div>
</Wrapper>
);
}
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
price | string | number | ✅ | — | Pricing value to be displayed prominently. |
planType | string | ✅ | "" | Label for the pricing plan (e.g., "Starter", "Pro"). |
badgeText | string | ✅ | "" | Badge like "Popular" or "Best Value". |
duration | string | ✅ | "" | Time interval for the pricing (e.g., "per month"). |
description | string | ✅ | "" | Short explanation or offer under the price. |
btnText | string | ✅ | "" | Button text (e.g., "Get Started"). |
bottomText | string | ✅ | "" | Informational text below the CTA button. |
onClick | () => void | ✅ | undefined | Function triggered when the CTA button is clicked. |
className | string | 🟡 | "" | Additional Tailwind classes to apply to the outer wrapper. |
animate | boolean | 🟡 | true | Enables hover scale animation using Framer Motion. |