Pricing 3
Pricing 3 is a React and Tailwind CSS layout component from SmoothUI, licensed MIT. Copy it and paste it into your project.
SmoothUI
MIT
layout
What it is
Pricing 3 is a React and Tailwind CSS layout component from SmoothUI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i motion
Licence
This component comes from SmoothUI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Pricing 3
"use client";
import { cn } from "@/lib/utils";
import SmoothButton from "@/components/smoothui/smooth-button";
import { motion, useReducedMotion } from "motion/react";
import { useEffect, useRef, useState } from "react";
const CARD_ANIMATION_DELAY = 0.1;
interface PriceFlowProps {
className?: string;
value: number;
}
function PriceFlow({ value, className = "" }: PriceFlowProps) {
const [prevValue, setPrevValue] = useState(value);
const prevTensRef = useRef<HTMLSpanElement>(null);
const nextTensRef = useRef<HTMLSpanElement>(null);
const prevOnesRef = useRef<HTMLSpanElement>(null);
const nextOnesRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (value !== prevValue) {
const prevTens = prevTensRef.current;
const nextTens = nextTensRef.current;
const prevOnes = prevOnesRef.current;
const nextOnes = nextOnesRef.current;
if (
prevTens &&
nextTens &&
Math.floor(value / 10) !== Math.floor(prevValue / 10)
) {
if (Math.floor(value / 10) > Math.floor(prevValue / 10)) {
prevTens.classList.add("slide-out-up");
nextTens.classList.add("slide-in-up");
} else {
prevTens.classList.add("slide-out-down");
nextTens.classList.add("slide-in-down");
}
const handleTensAnimationEnd = () => {
prevTens.classList.remove("slide-out-up", "slide-out-down");
nextTens.classList.remove("slide-in-up", "slide-in-down");
prevTens.removeEventListener("animationend", handleTensAnimationEnd);
};
prevTens.addEventListener("animationend", handleTensAnimationEnd);
}
if (prevOnes && nextOnes && value % 10 !== prevValue % 10) {
setTimeout(() => {
if (value % 10 > prevValue % 10) {
prevOnes.classList.add("slide-out-up");
nextOnes.classList.add("slide-in-up");
} else {
prevOnes.classList.add("slide-out-down");
nextOnes.classList.add("slide-in-down");
}
const handleOnesAnimationEnd = () => {
prevOnes.classList.remove("slide-out-up", "slide-out-down");
nextOnes.classList.remove("slide-in-up", "slide-in-down");
prevOnes.removeEventListener(
"animationend",
handleOnesAnimationEnd
);
};
prevOnes.addEventListener("animationend", handleOnesAnimationEnd);
}, 50);
}
setPrevValue(value);
}
}, [value, prevValue]);
const formatValue = (val: number) => val.toString().padStart(2, "0");
const prevFormatted = formatValue(prevValue);
const currentFormatted = formatValue(value);
return (
<span className={cn("relative inline-flex items-center", className)}>
<span className="relative inline-block overflow-hidden">
<span
className="absolute inset-0 flex items-center justify-center"
ref={prevTensRef}
style={{ transform: "translateY(-100%)" }}
>
{prevFormatted[0]}
</span>
<span
className="flex items-center justify-center"
ref={nextTensRef}
style={{ transform: "translateY(0%)" }}
>
{currentFormatted[0]}
</span>
</span>
<span className="relative inline-block overflow-hidden">
<span
className="absolute inset-0 flex items-center justify-center"
ref={prevOnesRef}
style={{ transform: "translateY(-100%)" }}
>
{prevFormatted[1]}
</span>
<span
className="flex items-center justify-center"
ref={nextOnesRef}
style={{ transform: "translateY(0%)" }}
>
{currentFormatted[1]}
</span>
</span>
</span>
);
}
export function PricingCreative() {
const shouldReduceMotion = useReducedMotion();
const [isAnnual, setIsAnnual] = useState(true);
return (
<section>
<div className="relative bg-muted/50 py-16 md:py-32">
<div className="mx-auto max-w-5xl px-6">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-balance font-bold text-3xl md:text-4xl lg:text-5xl lg:tracking-tight">
Deploy faster with Vercel Pro
</h2>
<p className="mx-auto mt-4 max-w-xl text-balance text-foreground/70 text-lg">
Scale your applications with advanced deployment features and
priority support
</p>
<div className="my-12">
<div
className="relative mx-auto grid w-fit grid-cols-2 rounded-full border bg-background p-1 *:block *:h-8 *:w-24 *:rounded-full *:text-foreground *:text-sm *:hover:opacity-75"
data-period={isAnnual ? "annually" : "monthly"}
>
<div
aria-hidden="true"
className={`pointer-events-none absolute inset-1 w-1/2 rounded-full border border-transparent bg-brand shadow ring-1 ring-foreground/5 transition-transform duration-500 ease-in-out ${
isAnnual ? "translate-x-full" : "translate-x-0"
}`}
/>
<button
className="relative duration-500 data-[active=true]:font-medium data-[active=true]:text-white"
data-active={!isAnnual}
onClick={() => setIsAnnual(false)}
type="button"
>
Monthly
</button>
<button
className="relative duration-500 data-[active=true]:font-medium data-[active=true]:text-white"
data-active={isAnnual}
onClick={() => setIsAnnual(true)}
type="button"
>
Annually
</button>
</div>
</div>
</div>
<div className="container">
<div className="mx-auto max-w-5xl">
<div className="grid grid-cols-2 gap-6 *:p-8">
{/* Free Plan */}
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
className="group relative flex h-[650px] cursor-pointer flex-col overflow-hidden rounded-2xl border bg-background p-8"
data-animate-card
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 40 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { duration: 0.3, ease: [0.22, 1, 0.36, 1] }
}
>
<div className="card-content relative z-10 flex h-full flex-col">
{/* Title */}
<h3 className="mb-4 font-bold text-2xl text-foreground">
Hobby
</h3>
{/* Price & Duration */}
<div className="mb-6">
<span className="font-semibold text-3xl text-foreground">
<PriceFlow value={0} />€
</span>
<span className="mx-2 text-foreground/70">•</span>
<span className="text-foreground/70">
Perfect for personal projects
</span>
</div>
{/* CTA Button */}
<SmoothButton className="mb-6 w-full" variant="outline">
Get Started
</SmoothButton>
{/* Description */}
<p className="mb-6 flex-grow text-foreground/70 text-sm leading-relaxed">
Deploy your personal projects and experiment with the
latest web technologies. Perfect for developers learning
and building side projects.
</p>
{/* What's Included */}
<div className="space-y-4">
<h4 className="font-medium text-foreground/70 text-xs uppercase tracking-wider">
What's included:
</h4>
<ul className="space-y-3">
{[
"100GB Bandwidth",
"Unlimited Personal Projects",
"Community Support",
"Automatic HTTPS",
"Global CDN",
"Preview Deployments",
].map((item) => (
<li
className="flex items-center gap-3 text-foreground text-sm"
key={item}
>
<div className="flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-foreground">
<svg
aria-hidden="true"
className="h-2 w-2 text-background"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
clipRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
fillRule="evenodd"
/>
</svg>
</div>
{item}
</li>
))}
</ul>
</div>
</div>
</motion.div>
{/* Pro Plan */}
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
className="group relative flex h-[650px] cursor-pointer flex-col overflow-hidden rounded-2xl border bg-primary p-8"
data-animate-card
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 40 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: {
delay: CARD_ANIMATION_DELAY,
duration: 0.3,
ease: [0.22, 1, 0.36, 1],
}
}
>
{/* Gradient Accent */}
<div className="gradient-accent absolute top-0 right-0 h-4 w-32 rounded-bl-2xl bg-gradient-to-r from-purple-400 via-pink-400 to-orange-400" />
<div className="card-content relative z-10 flex h-full flex-col">
{/* Title */}
<h3 className="mb-4 font-bold text-2xl text-foreground">
Pro
</h3>
{/* Price & Duration */}
<div className="mb-6">
<span className="font-semibold text-3xl text-foreground">
<PriceFlow value={isAnnual ? 12 : 25} />€
</span>
<span className="mx-2 text-foreground/70">•</span>
<span className="text-foreground/70">Per month</span>
</div>
{/* CTA Button */}
<SmoothButton className="mb-6 w-full" variant="candy">
Get Started
</SmoothButton>
{/* Description */}
<p className="mb-6 flex-grow text-foreground/70 text-sm leading-relaxed">
Advanced deployment features for production applications.
Perfect for teams building and scaling professional web
applications.
</p>
{/* What's Included */}
<div className="space-y-4">
<h4 className="font-medium text-foreground/70 text-xs uppercase tracking-wider">
What's included:
</h4>
<ul className="space-y-3">
{[
"Everything in Hobby",
"Unlimited Bandwidth",
"Team Collaboration",
"Priority Support",
"Advanced Analytics",
"Password Protection",
"Custom Domains",
"Edge Functions",
"Cron Jobs",
"Webhooks",
].map((item) => (
<li
className="flex items-center gap-3 text-foreground text-sm"
key={item}
>
<div className="flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-foreground">
<svg
aria-hidden="true"
className="h-2 w-2 text-background"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
clipRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
fillRule="evenodd"
/>
</svg>
</div>
{item}
</li>
))}
</ul>
</div>
</div>
</motion.div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
export default PricingCreative;