Docs
Animated Gradient Hero

Animated Gradient Hero

A stunning hero section with animated mesh gradients and flowing text effects. Perfect for modern SaaS, tech startups, and premium products. Features smooth canvas animations with warm orange/amber/yellow color scheme.

New: AI-Powered Features

Build faster
ship smarter

The complete platform for modern teams. Ship products faster with tools designed for scale and built for the future.

No credit card required
14-day free trial
Cancel anytime
4.9/5(2,500+ reviews)
Trusted by teams at
Vercel
GitHub
Linear
Stripe
Notion
50K+
Active Users
99.9%
Uptime
4.9/5
Rating

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";
 
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[];
  };
}
 
export function GradientHeroAnimated({
  badge = {
    icon: <Sparkles className="h-4 w-4" />,
    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,
}: 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 time = 0;
 
    const resize = () => {
      canvas.width = canvas.offsetWidth * window.devicePixelRatio;
      canvas.height = canvas.offsetHeight * window.devicePixelRatio;
      ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
    };
 
    resize();
    window.addEventListener("resize", resize);
 
    const animate = () => {
      time += 0.002;
 
      const width = canvas.offsetWidth;
      const height = canvas.offsetHeight;
 
      ctx.clearRect(0, 0, width, height);
 
      // Large orange orb
      const gradient1 = ctx.createRadialGradient(
        width * (0.25 + Math.sin(time * 0.5) * 0.1),
        height * (0.4 + Math.cos(time * 0.3) * 0.1),
        0,
        width * (0.25 + Math.sin(time * 0.5) * 0.1),
        height * (0.4 + Math.cos(time * 0.3) * 0.1),
        width * 0.4,
      );
 
      gradient1.addColorStop(0, "rgba(251, 146, 60, 0.35)");
      gradient1.addColorStop(0.5, "rgba(249, 115, 22, 0.2)");
      gradient1.addColorStop(1, "rgba(0, 0, 0, 0)");
 
      ctx.fillStyle = gradient1;
      ctx.fillRect(0, 0, width, height);
 
      // Amber center orb
      const gradient2 = ctx.createRadialGradient(
        width * (0.6 + Math.cos(time * 0.4) * 0.08),
        height * (0.5 + Math.sin(time * 0.6) * 0.08),
        0,
        width * (0.6 + Math.cos(time * 0.4) * 0.08),
        height * (0.5 + Math.sin(time * 0.6) * 0.08),
        width * 0.35,
      );
 
      gradient2.addColorStop(0, "rgba(245, 158, 11, 0.4)");
      gradient2.addColorStop(0.5, "rgba(251, 191, 36, 0.2)");
      gradient2.addColorStop(1, "rgba(0, 0, 0, 0)");
 
      ctx.fillStyle = gradient2;
      ctx.fillRect(0, 0, width, height);
 
      // Yellow accent orb
      const gradient3 = ctx.createRadialGradient(
        width * (0.75 + Math.sin(time * 0.7) * 0.1),
        height * (0.3 + Math.cos(time * 0.5) * 0.1),
        0,
        width * (0.75 + Math.sin(time * 0.7) * 0.1),
        height * (0.3 + Math.cos(time * 0.5) * 0.1),
        width * 0.3,
      );
 
      gradient3.addColorStop(0, "rgba(250, 204, 21, 0.3)");
      gradient3.addColorStop(0.5, "rgba(234, 179, 8, 0.15)");
      gradient3.addColorStop(1, "rgba(0, 0, 0, 0)");
 
      ctx.fillStyle = gradient3;
      ctx.fillRect(0, 0, width, height);
 
      animationFrameId = requestAnimationFrame(animate);
    };
 
    animate();
 
    return () => {
      window.removeEventListener("resize", resize);
      cancelAnimationFrame(animationFrameId);
    };
  }, []);
 
  const handlePrimaryCTA = () => {
    if (primaryCTA.onClick) {
      primaryCTA.onClick();
    } else if (primaryCTA.href) {
      window.location.href = primaryCTA.href;
    }
  };
 
  const handleSecondaryCTA = () => {
    if (secondaryCTA?.onClick) {
      secondaryCTA.onClick();
    } else if (secondaryCTA?.href) {
      window.location.href = secondaryCTA.href;
    }
  };
 
  return (
    <section className="relative flex min-h-screen items-center overflow-hidden bg-neutral-950">
      <canvas
        ref={canvasRef}
        className="absolute inset-0 h-full w-full"
        style={{ filter: "blur(90px)" }}
      />
 
      <div className="absolute inset-0 bg-gradient-to-t from-neutral-950 via-transparent to-neutral-950/50" />
 
      <div className="container relative z-10 mx-auto px-6 py-24">
        <div className="mx-auto max-w-4xl text-center">
          {badge && (
            <div className="mb-8 inline-flex animate-in fade-in slide-in-from-bottom-3 items-center gap-2 rounded-full border border-orange-500/20 bg-orange-500/10 px-4 py-2 backdrop-blur-xl duration-700">
              <span className="text-orange-400">{badge.icon}</span>
              <span className="text-sm font-medium text-white/90">
                {badge.text}
              </span>
            </div>
          )}
 
          <h1 className="mb-8 animate-in fade-in slide-in-from-bottom-4 text-6xl font-bold leading-[1.05] tracking-tight text-white duration-1000 sm:text-7xl md:text-8xl">
            {headline.line1}
            <br />
            <span className="animate-gradient bg-gradient-to-r from-orange-400 via-amber-300 to-yellow-400 bg-[length:200%_auto] bg-clip-text text-transparent">
              {headline.line2}
            </span>
          </h1>
 
          <p className="mb-8 animate-in fade-in slide-in-from-bottom-5 text-xl leading-relaxed text-neutral-400 duration-1000 sm:text-2xl">
            {description}
          </p>
 
          {features && features.length > 0 && (
            <div className="mb-12 flex animate-in fade-in slide-in-from-bottom-6 flex-wrap items-center justify-center gap-6 text-sm text-neutral-400 duration-1000">
              {features.map((feature, index) => (
                <div key={index} className="flex items-center gap-2">
                  <CheckCircle2 className="h-4 w-4 text-green-400" />
                  <span>{feature}</span>
                </div>
              ))}
            </div>
          )}
 
          <div className="flex animate-in fade-in slide-in-from-bottom-7 flex-col items-center justify-center gap-4 duration-1000 sm:flex-row">
            <Button
              size="lg"
              onClick={handlePrimaryCTA}
              className="group h-14 bg-gradient-to-r from-orange-500 to-amber-500 px-10 text-lg font-semibold text-white shadow-lg shadow-orange-500/25 transition-all hover:scale-[1.02] hover:shadow-xl hover:shadow-orange-500/40"
            >
              {primaryCTA.text}
              <ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
            </Button>
            {secondaryCTA && (
              <Button
                size="lg"
                variant="ghost"
                onClick={handleSecondaryCTA}
                className="group h-14 px-10 text-lg font-semibold text-neutral-300 transition-colors hover:bg-white/5 hover:text-white"
              >
                <Play className="mr-2 h-5 w-5 transition-transform group-hover:scale-110" />
                {secondaryCTA.text}
              </Button>
            )}
          </div>
 
          {socialProof && (
            <div className="mt-12 animate-in fade-in slide-in-from-bottom-8 duration-1000">
              <div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
                {socialProof.rating && (
                  <div className="flex items-center gap-2">
                    <div className="flex">
                      {Array.from({ length: 5 }).map((_, i) => (
                        <Star
                          key={i}
                          className={`h-5 w-5 ${
                            i < Math.floor(socialProof.rating!)
                              ? "fill-yellow-400 text-yellow-400"
                              : "fill-neutral-700 text-neutral-700"
                          }`}
                        />
                      ))}
                    </div>
                    <span className="text-sm font-semibold text-white">
                      {socialProof.rating}/5
                    </span>
                    {socialProof.reviewCount && (
                      <span className="text-sm text-neutral-500">
                        ({socialProof.reviewCount} reviews)
                      </span>
                    )}
                  </div>
                )}
                {socialProof.text && (
                  <span className="text-sm text-neutral-500">
                    {socialProof.text}
                  </span>
                )}
              </div>
              {socialProof.logos && socialProof.logos.length > 0 && (
                <div className="mt-8 flex flex-wrap items-center justify-center gap-8 opacity-50">
                  {socialProof.logos.map((logo, index) => (
                    <div
                      key={index}
                      className="text-lg font-semibold text-white transition-opacity hover:opacity-100"
                    >
                      {logo}
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
 
          {stats && stats.length > 0 && (
            <div className="mt-20 grid animate-in fade-in slide-in-from-bottom-9 grid-cols-3 gap-8 border-t border-white/5 pt-12 duration-1000">
              {stats.map((stat, index) => (
                <div
                  key={index}
                  className="group transition-transform hover:scale-105"
                >
                  <div className="mb-2 text-3xl font-bold text-white sm:text-4xl">
                    {stat.value}
                  </div>
                  <div className="text-sm text-neutral-500">{stat.label}</div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
 
      <div className="pointer-events-none absolute inset-0">
        <div className="absolute left-[15%] top-[25%] h-[400px] w-[400px] animate-float-slow rounded-full bg-orange-500/20 blur-[80px]" />
        <div className="absolute right-[20%] top-[35%] h-[350px] w-[350px] animate-float-delayed rounded-full bg-amber-500/20 blur-[80px]" />
        <div className="absolute bottom-[30%] left-[50%] h-[300px] w-[300px] -translate-x-1/2 animate-float rounded-full bg-yellow-500/10 blur-[80px]" />
      </div>
 
      <style jsx>{`
        @keyframes gradient {
          0%,
          100% {
            background-position: 0% 50%;
          }
          50% {
            background-position: 100% 50%;
          }
        }
        @keyframes float {
          0%,
          100% {
            transform: translate(0, 0);
          }
          33% {
            transform: translate(20px, -20px);
          }
          66% {
            transform: translate(-15px, 15px);
          }
        }
        @keyframes float-delayed {
          0%,
          100% {
            transform: translate(0, 0);
          }
          33% {
            transform: translate(-25px, 20px);
          }
          66% {
            transform: translate(20px, -15px);
          }
        }
        @keyframes float-slow {
          0%,
          100% {
            transform: translate(0, 0);
          }
          50% {
            transform: translate(15px, 15px);
          }
        }
        .animate-gradient {
          animation: gradient 8s ease infinite;
        }
        .animate-float {
          animation: float 15s ease-in-out infinite;
        }
        .animate-float-delayed {
          animation: float-delayed 18s ease-in-out infinite;
        }
        .animate-float-slow {
          animation: float-slow 20s ease-in-out infinite;
        }
      `}</style>
    </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

PropTypeDefaultDescription
badgeobject{ icon: <Sparkles />, text: "Introducing our new platform" }Optional badge at the top
badge.iconReact.ReactNode<Sparkles />Icon for the badge
badge.textstringRequiredBadge text
headlineobject{ line1: "Build the next", line2: "big thing" }Headline configuration
headline.line1stringRequiredFirst line of headline
headline.line2stringRequiredSecond line (gets animated gradient)
descriptionstringDefault textHero description
primaryCTAobjectRequiredPrimary call-to-action button
primaryCTA.textstringRequiredButton text
primaryCTA.hrefstringOptionalLink URL
primaryCTA.onClick() => voidOptionalClick handler
secondaryCTAobjectOptionalSecondary call-to-action button
secondaryCTA.textstringRequiredButton text
secondaryCTA.hrefstringOptionalLink URL
secondaryCTA.onClick() => voidOptionalClick handler
featuresstring[]["No credit card required", ...]Feature list with checkmarks
statsArray<{value: string, label: string}>Default statsStats to display
socialProofobjectOptionalSocial proof section
socialProof.ratingnumberOptionalStar rating (0-5)
socialProof.reviewCountstringOptionalNumber of reviews
socialProof.textstringOptionalSocial proof text
socialProof.logosstring[]OptionalCompany 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

  1. Color contrast - Ensure text remains readable against animated backgrounds
  2. Performance testing - Test on lower-end devices, consider reducing animation complexity for mobile
  3. Blur intensity - Adjust blur values if gradients are too intense or subtle
  4. Animation speed - Slower animations (15-20s) feel more premium than fast ones
  5. 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;
  }
}