Back to library
backgrounds
Animated Gradient (SVG)
Multi-color animated gradient background built from blurred SVG circles that drift around their container.
Inspired by danielpetho · View source
Preview
Revenue
$48.2k
Active users
12,904
Copy for AI
Copy for Lovable is the fastest way to move this component into another Lovable project — it copies the full prompt with dependencies, files, and a usage example. Copy all files gives you the raw source if you want to paste it manually.
Installation
npx shadcn@latest add https://21st.dev/r/danielpetho/animated-gradient-with-svgPaste into a terminal — or paste into a Lovable chat and I'll run it for you.
Source files
components/ui/animated-gradient-with-svg.tsx
import React, { useMemo, useRef } from "react";
import { cn } from "@/lib/utils";
import { useDimensions } from "./use-debounced-dimensions";
import "./animated-gradient-with-svg.css";
interface AnimatedGradientProps {
colors: string[];
speed?: number;
blur?: "light" | "medium" | "heavy";
}
/** Deterministic PRNG so SSR and client markup match. */
const makeRandom = (seed: number) => {
let s = seed + 0x6d2b79f5;
return () => {
s = Math.imul(s ^ (s >>> 15), s | 1);
s ^= s + Math.imul(s ^ (s >>> 7), s | 61);
return ((s ^ (s >>> 14)) >>> 0) / 4294967296;
};
};
const AnimatedGradient: React.FC<AnimatedGradientProps> = ({
colors,
speed = 5,
blur = "light",
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const dimensions = useDimensions(containerRef);
const circleSize = useMemo(
() => Math.max(dimensions.width, dimensions.height),
[dimensions.width, dimensions.height],
);
const blurClass =
blur === "light"
? "blur-2xl"
: blur === "medium"
? "blur-3xl"
: "blur-[100px]";
const circles = useMemo(
() =>
colors.map((color, index) => {
const rand = makeRandom(index * 9973 + color.length * 131);
return {
color,
top: rand() * 50,
left: rand() * 50,
tx1: rand() - 0.5,
ty1: rand() - 0.5,
tx2: rand() - 0.5,
ty2: rand() - 0.5,
tx3: rand() - 0.5,
ty3: rand() - 0.5,
tx4: rand() - 0.5,
ty4: rand() - 0.5,
scale: 0.5 + rand(),
};
}),
[colors],
);
return (
<div ref={containerRef} className="absolute inset-0 overflow-hidden">
<div className={cn(`absolute inset-0`, blurClass)}>
{circles.map((c, index) => (
<svg
key={index}
className="absolute animate-background-gradient"
style={
{
top: `${c.top}%`,
left: `${c.left}%`,
"--background-gradient-speed": `${1 / speed}`,
"--tx-1": c.tx1,
"--ty-1": c.ty1,
"--tx-2": c.tx2,
"--ty-2": c.ty2,
"--tx-3": c.tx3,
"--ty-3": c.ty3,
"--tx-4": c.tx4,
"--ty-4": c.ty4,
} as React.CSSProperties
}
width={circleSize * c.scale}
height={circleSize * c.scale}
viewBox="0 0 100 100"
>
<circle
cx="50"
cy="50"
r="50"
fill={c.color}
className="opacity-30 dark:opacity-[0.15]"
/>
</svg>
))}
</div>
</div>
);
};
export { AnimatedGradient };