Build faster ship smarter
Features
- ✅ Fully prop-based - Customize everything via props
- ✅ TypeScript support - Full type safety
- ✅ Animated canvas gradients - Smooth, hardware-accelerated
- ✅ Flowing text gradient - Animated headline effect
- ✅ Floating orb effects - Three independent animations
- ✅ Staggered entrance - Sequential fade-in animations
- ✅ Optional sections - Badge, features, stats, social proof
- ✅ Flexible CTAs - Support for href or onClick
- ✅ Star ratings - Built-in rating display
- ✅ Company logos - Social proof section
- ✅ Fully responsive - Mobile-first design
- ✅ Performance optimized - 60fps animations
Installation
Copy and paste the following code into your project.
"use client";
import { Button } from "@/components/ui/button";
import { ArrowRight, CheckCircle2, Play, Sparkles, Star } from "lucide-react";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
interface GradientHeroAnimatedProps {
badge?: {
icon?: React.ReactNode;
text: string;
};
headline: {
line1: string;
line2: string;
};
description: string;
primaryCTA: {
text: string;
href?: string;
onClick?: () => void;
};
secondaryCTA?: {
text: string;
href?: string;
onClick?: () => void;
};
features?: string[];
stats?: Array<{
value: string;
label: string;
}>;
socialProof?: {
rating?: number;
reviewCount?: string;
text?: string;
logos?: string[];
};
className?: string;
}
export function GradientHeroAnimated({
badge = {
icon: <Sparkles className="h-3.5 w-3.5" />,
text: "Introducing our new platform",
},
headline = {
line1: "Build the next",
line2: "big thing",
},
description = "Ship products faster with tools designed for modern teams. From idea to launch in record time.",
primaryCTA = {
text: "Get Started Free",
href: "#",
},
secondaryCTA = {
text: "Watch Demo",
href: "#",
},
features = ["No credit card required", "14-day free trial", "Cancel anytime"],
stats = [
{ value: "50K+", label: "Active Users" },
{ value: "99.9%", label: "Uptime" },
{ value: "4.9/5", label: "Rating" },
],
socialProof,
className,
}: GradientHeroAnimatedProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
let animationFrameId: number;
let t = 0;
const resize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener("resize", resize);
resize();
const animate = () => {
t += 0.005;
const w = canvas.width;
const h = canvas.height;
ctx.clearRect(0, 0, w, h);
// Create a complex mesh gradient effect
const gradient = ctx.createLinearGradient(0, 0, w, h);
gradient.addColorStop(0, "#0a0a0a");
gradient.addColorStop(1, "#171717");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// Organic flowing shapes
const particles = [
{
x: w * 0.2 + Math.sin(t * 0.5) * w * 0.1,
y: h * 0.2 + Math.cos(t * 0.3) * h * 0.1,
r: w * 0.4,
color: "rgba(249, 115, 22, 0.15)", // Orange
},
{
x: w * 0.8 - Math.sin(t * 0.4) * w * 0.1,
y: h * 0.4 + Math.cos(t * 0.5) * h * 0.1,
r: w * 0.35,
color: "rgba(234, 179, 8, 0.12)", // Yellow
},
{
x: w * 0.5 + Math.sin(t * 0.6) * w * 0.15,
y: h * 0.8 - Math.cos(t * 0.4) * h * 0.1,
r: w * 0.45,
color: "rgba(245, 158, 11, 0.1)", // Amber
},
];
particles.forEach((p) => {
ctx.beginPath();
const g = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
g.addColorStop(0, p.color);
g.addColorStop(1, "rgba(0,0,0,0)");
ctx.fillStyle = g;
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fill();
});
// Subtle noise overlay simulation
ctx.globalCompositeOperation = "overlay";
ctx.fillStyle = "rgba(255,255,255,0.03)";
for (let i = 0; i < w; i += 4) {
for (let j = 0; j < h; j += 4) {
if (Math.random() > 0.5) {
ctx.fillRect(i, j, 1, 1);
}
}
}
ctx.globalCompositeOperation = "source-over";
animationFrameId = requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener("resize", resize);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<section
className={cn(
"relative min-h-screen w-full overflow-hidden bg-neutral-950 font-sans",
className,
)}
>
{/* Animated Background */}
<canvas ref={canvasRef} className="absolute inset-0 h-full w-full" />
{/* Vignette & Overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-neutral-950/50 via-transparent to-neutral-950 pointer-events-none" />
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-20 mix-blend-soft-light pointer-events-none" />
<div className="relative z-10 container mx-auto px-4 pt-32 pb-20 md:pt-40 md:pb-32">
<div className="mx-auto max-w-5xl text-center">
{/* Badge */}
{badge && (
<div className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-1.5 text-sm font-medium text-white/80 backdrop-blur-md transition-all hover:bg-white/10 hover:border-white/20 animate-in fade-in slide-in-from-bottom-4 duration-700 mb-8 cursor-default">
<span className="flex items-center justify-center rounded-full bg-orange-500/20 p-1 text-orange-400">
{badge.icon}
</span>
{badge.text}
</div>
)}
{/* Headline */}
<h1 className="mx-auto mb-8 max-w-4xl text-5xl font-bold tracking-tight text-white sm:text-7xl md:text-8xl lg:leading-[1.1] animate-in fade-in slide-in-from-bottom-6 duration-1000">
{headline.line1}{" "}
<span className="relative whitespace-nowrap">
<span className="absolute -inset-1 -z-10 block -skew-y-2 bg-gradient-to-r from-orange-500/20 to-amber-500/20 blur-xl" />
<span className="bg-gradient-to-r from-orange-400 via-amber-300 to-yellow-400 bg-clip-text text-transparent">
{headline.line2}
</span>
</span>
</h1>
{/* Description */}
<p className="mx-auto mb-12 max-w-2xl text-lg text-neutral-400 sm:text-xl md:text-2xl md:leading-relaxed animate-in fade-in slide-in-from-bottom-8 duration-1000 delay-200">
{description}
</p>
{/* CTAs */}
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row animate-in fade-in slide-in-from-bottom-10 duration-1000 delay-300">
<Button
size="lg"
onClick={
primaryCTA.onClick ||
(() =>
primaryCTA.href && (window.location.href = primaryCTA.href))
}
className="group relative h-14 overflow-hidden rounded-full bg-white px-8 text-lg font-semibold text-neutral-950 transition-all hover:scale-105 hover:bg-neutral-200"
>
<span className="relative z-10 flex items-center gap-2">
{primaryCTA.text}
<ArrowRight className="h-5 w-5 transition-transform group-hover:translate-x-1" />
</span>
<div className="absolute inset-0 -z-10 bg-gradient-to-r from-orange-100 to-amber-100 opacity-0 transition-opacity group-hover:opacity-100" />
</Button>
{secondaryCTA && (
<Button
size="lg"
variant="ghost"
onClick={
secondaryCTA.onClick ||
(() =>
secondaryCTA.href &&
(window.location.href = secondaryCTA.href))
}
className="group h-14 rounded-full border border-white/10 bg-white/5 px-8 text-lg font-semibold text-white backdrop-blur-sm transition-all hover:bg-white/10 hover:border-white/20"
>
<Play className="mr-2 h-5 w-5 fill-white/20 transition-transform group-hover:scale-110" />
{secondaryCTA.text}
</Button>
)}
</div>
{/* Features */}
{features && features.length > 0 && (
<div className="mt-12 flex flex-wrap justify-center gap-x-8 gap-y-4 text-sm text-neutral-500 animate-in fade-in slide-in-from-bottom-12 duration-1000 delay-400">
{features.map((feature, i) => (
<div key={i} className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-orange-500" />
<span>{feature}</span>
</div>
))}
</div>
)}
{/* Social Proof & Stats */}
{stats && stats.length > 0 && (
<div className="mt-24 border-t border-white/5 pt-12 animate-in fade-in slide-in-from-bottom-12 duration-1000 delay-500">
<div className="grid grid-cols-3 gap-8 text-center">
{stats.map((stat, i) => (
<div key={i} className="group">
<div className="text-3xl font-bold text-white transition-colors group-hover:text-orange-400">
{stat.value}
</div>
<div className="text-sm font-medium text-neutral-500">
{stat.label}
</div>
</div>
))}
</div>
</div>
)}
{/* Social Proof */}
{socialProof && (
<div className="mt-16 space-y-8 animate-in fade-in slide-in-from-bottom-12 duration-1000 delay-600">
{(socialProof.rating || socialProof.text) && (
<div className="flex flex-col items-center gap-3">
{socialProof.rating && (
<div className="flex items-center gap-2">
<div className="flex">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={cn(
"h-4 w-4",
i < Math.floor(socialProof.rating!)
? "fill-orange-500 text-orange-500"
: "fill-neutral-800 text-neutral-800",
)}
/>
))}
</div>
<span className="text-sm font-medium text-white">
{socialProof.rating}/5
</span>
{socialProof.reviewCount && (
<span className="text-sm text-neutral-500">
({socialProof.reviewCount} reviews)
</span>
)}
</div>
)}
{socialProof.text && (
<p className="text-xs text-neutral-500 uppercase tracking-widest">
{socialProof.text}
</p>
)}
</div>
)}
{socialProof.logos && socialProof.logos.length > 0 && (
<div className="flex flex-wrap items-center justify-center gap-8 opacity-40 grayscale transition-all duration-500 hover:opacity-100 hover:grayscale-0">
{socialProof.logos.map((logo, i) => (
<span key={i} className="text-lg font-bold text-white">
{logo}
</span>
))}
</div>
)}
</div>
)}
</div>
</div>
</section>
);
}Update the import paths to match your project setup.
import { Button } from "@/components/ui/button";Usage
Basic Usage
import GradientHeroAnimated from "@/components/ui/gradient-hero-animated";
export default function HomePage() {
return <GradientHeroAnimated />;
}With Custom Props
import GradientHeroAnimated from "@/components/ui/gradient-hero-animated";
import { Sparkles } from "lucide-react";
export default function HomePage() {
return (
<GradientHeroAnimated
badge={{
icon: <Sparkles className="h-4 w-4" />,
text: "New: AI-Powered Features",
}}
headline={{
line1: "Build faster",
line2: "ship smarter",
}}
description="The complete platform for modern teams. Ship products faster with tools designed for scale."
primaryCTA={{
text: "Start Free Trial",
onClick: () => console.log("Primary CTA clicked"),
}}
secondaryCTA={{
text: "Watch Demo",
href: "/demo",
}}
features={[
"No credit card required",
"14-day free trial",
"Cancel anytime",
]}
stats={[
{ value: "100K+", label: "Active Users" },
{ value: "99.9%", label: "Uptime" },
{ value: "4.9/5", label: "Rating" },
]}
socialProof={{
rating: 4.9,
reviewCount: "2,500+",
text: "Trusted by teams worldwide",
logos: ["Google", "Microsoft", "Amazon", "Netflix"],
}}
/>
);
}With Router Navigation
"use client";
import { useRouter } from "next/navigation";
import GradientHeroAnimated from "@/components/ui/gradient-hero-animated";
export default function HomePage() {
const router = useRouter();
return (
<GradientHeroAnimated
headline={{
line1: "Build the next",
line2: "big thing",
}}
description="Ship products faster with tools designed for modern teams."
primaryCTA={{
text: "Get Started Free",
onClick: () => router.push("/signup"),
}}
secondaryCTA={{
text: "Learn More",
onClick: () => router.push("/about"),
}}
/>
);
}Props
GradientHeroAnimatedProps
| Prop | Type | Default | Description |
|---|---|---|---|
badge | object | { icon: <Sparkles />, text: "Introducing our new platform" } | Optional badge at the top |
badge.icon | React.ReactNode | <Sparkles /> | Icon for the badge |
badge.text | string | Required | Badge text |
headline | object | { line1: "Build the next", line2: "big thing" } | Headline configuration |
headline.line1 | string | Required | First line of headline |
headline.line2 | string | Required | Second line (gets animated gradient) |
description | string | Default text | Hero description |
primaryCTA | object | Required | Primary call-to-action button |
primaryCTA.text | string | Required | Button text |
primaryCTA.href | string | Optional | Link URL |
primaryCTA.onClick | () => void | Optional | Click handler |
secondaryCTA | object | Optional | Secondary call-to-action button |
secondaryCTA.text | string | Required | Button text |
secondaryCTA.href | string | Optional | Link URL |
secondaryCTA.onClick | () => void | Optional | Click handler |
features | string[] | ["No credit card required", ...] | Feature list with checkmarks |
stats | Array<{value: string, label: string}> | Default stats | Stats to display |
socialProof | object | Optional | Social proof section |
socialProof.rating | number | Optional | Star rating (0-5) |
socialProof.reviewCount | string | Optional | Number of reviews |
socialProof.text | string | Optional | Social proof text |
socialProof.logos | string[] | Optional | Company logos |
Customization
Minimal Example
<GradientHeroAnimated
headline={{
line1: "Simple",
line2: "and clean",
}}
description="Less is more."
primaryCTA={{
text: "Get Started",
href: "/signup",
}}
features={undefined}
stats={undefined}
socialProof={undefined}
/>With All Features
<GradientHeroAnimated
badge={{ text: "🎉 Product Hunt #1 Product of the Day" }}
headline={{
line1: "The future of",
line2: "productivity",
}}
description="Everything you need to build, ship, and scale your product. Trusted by over 50,000 teams worldwide."
primaryCTA={{
text: "Start Free Trial",
onClick: handleSignup,
}}
secondaryCTA={{
text: "Schedule Demo",
onClick: handleDemo,
}}
features={[
"Free 14-day trial",
"No credit card required",
"Cancel anytime",
"24/7 support",
]}
stats={[
{ value: "50K+", label: "Active Users" },
{ value: "99.9%", label: "Uptime SLA" },
{ value: "4.9/5", label: "User Rating" },
]}
socialProof={{
rating: 4.9,
reviewCount: "5,000+",
text: "Loved by teams at",
logos: ["Stripe", "Vercel", "Linear", "GitHub", "Notion"],
}}
/>Change Gradient Colors
To change the animated gradient colors, you'll need to modify the canvas gradient stops in the component. Here are some preset color schemes:
Orange/Amber/Yellow (Default):
gradient1.addColorStop(0, "rgba(251, 146, 60, 0.35)");
gradient2.addColorStop(0, "rgba(245, 158, 11, 0.4)");
gradient3.addColorStop(0, "rgba(250, 204, 21, 0.3)");Purple/Pink/Blue:
gradient1.addColorStop(0, "rgba(139, 92, 246, 0.35)");
gradient2.addColorStop(0, "rgba(236, 72, 153, 0.4)");
gradient3.addColorStop(0, "rgba(59, 130, 246, 0.3)");Green/Teal/Cyan:
gradient1.addColorStop(0, "rgba(16, 185, 129, 0.35)");
gradient2.addColorStop(0, "rgba(20, 184, 166, 0.4)");
gradient3.addColorStop(0, "rgba(6, 182, 212, 0.3)");Red/Orange/Pink:
gradient1.addColorStop(0, "rgba(239, 68, 68, 0.35)");
gradient2.addColorStop(0, "rgba(249, 115, 22, 0.4)");
gradient3.addColorStop(0, "rgba(236, 72, 153, 0.3)");Best Practices
- Color contrast - Ensure text remains readable against animated backgrounds
- Performance testing - Test on lower-end devices, consider reducing animation complexity for mobile
- Blur intensity - Adjust blur values if gradients are too intense or subtle
- Animation speed - Slower animations (15-20s) feel more premium than fast ones
- Content hierarchy - Keep the layout clean and focused on the main message
Use Cases
Perfect for:
- Modern SaaS platforms
- Tech startups
- AI/ML products
- Creative tools
- Design software
- Developer platforms
- Premium services
- Product launches
Examples
Similar styles used by:
- Vercel - Deployment platform with animated gradients
- Stripe - Payment platform with elegant animations
- Linear - Issue tracking with smooth transitions
- Framer - Design tool with flowing gradients
TypeScript Interface
interface GradientHeroAnimatedProps {
badge?: {
icon?: React.ReactNode;
text: string;
};
headline: {
line1: string;
line2: string; // This line gets the animated gradient
};
description: string;
primaryCTA: {
text: string;
href?: string;
onClick?: () => void;
};
secondaryCTA?: {
text: string;
href?: string;
onClick?: () => void;
};
features?: string[];
stats?: Array<{
value: string;
label: string;
}>;
socialProof?: {
rating?: number;
reviewCount?: string;
text?: string;
logos?: string[];
};
}@media (prefers-reduced-motion: reduce) {
.animate-gradient,
.animate-float,
.animate-float-delayed,
.animate-float-slow {
animation: none;
}
}