Installation
Copy the following code to your project.
import React from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import {
Code2,
Database,
GitBranch,
Terminal,
LucideIcon,
Blocks,
Braces,
Bug,
FileCode2,
Github,
Container,
Webhook,
PackageOpen,
Globe2,
} from "lucide-react";
interface CircuitPulseProps {
className?: string;
color?: string;
}
const pulseVariants = {
dot1: {
initial: { y: "-50%", x: "-50%" },
animate: {
y: ["-50%", "30%", "30%"],
transition: {
duration: 5,
times: [0, 0.3, 1],
repeat: Infinity,
repeatDelay: 2,
ease: "easeInOut",
},
},
},
dot2: {
initial: { y: "-50%", x: "-50%" },
animate: {
y: ["-50%", "9.5%", "9.5%"],
x: ["-50%", "-50%", "9.5%"],
transition: {
duration: 4,
times: [0, 0.5, 1],
repeat: Infinity,
repeatDelay: 1.5,
ease: "easeInOut",
},
},
},
dot3: {
initial: { y: "-50%", x: "-50%" },
animate: {
y: ["-50%", "30%", "30%"],
transition: {
duration: 3.5,
times: [0, 0.4, 1],
repeat: Infinity,
repeatDelay: 2.5,
ease: "easeInOut",
},
},
},
dot4: {
initial: { y: "-50%", x: "-50%" },
animate: {
y: ["-50%", "9.5%", "9.5%"],
x: ["-50%", "-50%", "9.5%"],
transition: {
duration: 4.5,
times: [0, 0.6, 1],
repeat: Infinity,
repeatDelay: 1.8,
ease: "easeInOut",
},
},
},
glow: {
initial: { opacity: 0.3 },
animate: {
opacity: [0.3, 0.6, 0.3],
transition: { duration: 2, repeat: Infinity },
},
},
};
interface IconConfig {
Icon: LucideIcon;
position: string;
}
const icons: IconConfig[] = [
{ Icon: Github, position: "rotate-0" },
{ Icon: Code2, position: "rotate-45" },
{ Icon: Terminal, position: "rotate-90" },
{ Icon: Database, position: "rotate-135" },
{ Icon: Blocks, position: "rotate-180" },
{ Icon: Container, position: "rotate-[225deg]" },
{ Icon: FileCode2, position: "rotate-[270deg]" },
{ Icon: PackageOpen, position: "rotate-[315deg]" },
];
export function CircuitPulse({
className,
color = "#ff4545",
}: CircuitPulseProps) {
return (
<div className={cn("relative h-[80vmin] w-[80vmin]", className)}>
<div className="absolute inset-0 overflow-hidden rounded-3xl"></div>
{[...Array(8)].map((_, i) => (
<div
key={i}
className={cn(
"pointer-events-none absolute h-1/2 w-1/2",
i < 4
? "left-1/2 top-0 origin-bottom -translate-x-1/2"
: "left-0 top-0 origin-bottom-right",
i === 1 && "rotate-90",
i === 2 && "rotate-180",
i === 3 && "rotate-[270deg]",
i === 5 && "-scale-x-100",
i === 6 && "-scale-100",
i === 7 && "-scale-y-100",
)}
>
<motion.div
className={cn(
"opacity-50",
i < 4
? "h-full w-[calc(50%+1px)] border-r"
: "absolute left-[calc(30%-1px)] top-[30%] h-[60%] w-[70%] rounded-br-xl border-b border-l",
)}
style={{ borderColor: color }}
variants={pulseVariants.glow}
initial="initial"
animate="animate"
/>
<motion.div
className={cn(
"absolute h-full w-full",
i < 2
? "left-1/2 top-[10%]"
: i < 4
? "left-1/2 top-[10%]"
: i < 6
? "left-[30%] top-[30%]"
: "left-[30%] top-[30%]",
)}
variants={
i < 2
? pulseVariants.dot1
: i < 4
? pulseVariants.dot3
: i < 6
? pulseVariants.dot2
: pulseVariants.dot4
}
initial="initial"
animate="animate"
>
<motion.div
className="absolute left-1/2 top-1/2 h-[9px] w-[9px] -translate-x-1/2 -translate-y-1/2 rounded-full"
style={{
backgroundColor: color,
boxShadow: `0 0 10px ${color}`,
}}
variants={pulseVariants.glow}
/>
</motion.div>
<div
className={cn(
"absolute flex h-[40px] w-[40px] items-center justify-center rounded-full",
i < 4
? "left-1/2 top-0 -translate-x-1/2"
: "left-[30%] top-[30%] -translate-x-1/2 -translate-y-1/2",
)}
style={{
backgroundColor: color,
boxShadow: `0 0 15px ${color}`,
}}
>
<div className={icons[i].position}>
{React.createElement(icons[i].Icon, {
className: "h-5 w-5 text-white",
})}
</div>
</div>
</div>
))}
<div className="absolute left-1/2 top-1/2 h-[25%] w-[25%] -translate-x-1/2 -translate-y-1/2">
<motion.div
className="absolute left-1/2 top-1/2 h-full w-full -translate-x-1/2 -translate-y-1/2 rounded-full bg-black/90 shadow-lg"
variants={pulseVariants.glow}
initial="initial"
animate="animate"
/>
<motion.div
className="absolute left-1/2 top-1/2 flex h-32 w-32 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full"
style={{
backgroundColor: color,
boxShadow: `0 0 20px ${color}`,
}}
>
<Globe2 className="size-16 text-white" />
</motion.div>
</div>
</div>
);
}
Update the import paths to match your project setup.
import CircuitPulse from "@/components/ui/circuit-pulse";
Usage
export default function Example() {
return (
<div className="relative min-h-[400px]">
<CircuitPulse />
</div>
);
}
Examples
Custom Color
You can customize the color of the circuit paths and dots.
<CircuitPulse color="#4545ff" />
Background Integration
For the best visual effect, place the component in a container with a gradient background:
<div className="relative z-30 grid min-h-screen grid-cols-1 gap-8 overflow-hidden bg-linear-to-b from-background via-background to-black/50 p-8">
<div className="relative flex min-h-[400px] items-center justify-center">
<CircuitPulse />
</div>
</div>
Props
Property | Type | Default | Description |
---|---|---|---|
color | string | #ff4545 | The color of the circuit paths, dots, and icons. Accepts any valid CSS color value. |
className | string | - | Additional CSS classes to apply to the root element. |
Features
- Four unique dot animations with different timing patterns
- Glowing paths and dots with customizable colors
- Responsive design that scales with container
- Dark mode compatible
- Eight tech-themed icons arranged in a circuit pattern
- Smooth transitions and animations
Accessibility
The component is purely decorative and does not require any interaction. All animations are subtle and should not trigger motion sickness.