Animated electric frame effect for dramatic emphasis — add to your site in seconds
Electric Border
In case you'd like to emphasize something very dramatically.
Copy URL or Code
Paste to your AI coding assistant and customize:
Done. Your AI handles the rest.
Fully customizable. Adjust colors, speed, displacement, and dimensions. Works great for hero sections, feature highlights, or any dramatic emphasis.
/*
================================================================================
AI COMPONENT: ElectricBorder
================================================================================
SETUP:
1. Create file: components/ElectricBorder.tsx
2. Copy this entire code block into that file
3. Import: import ElectricBorder from "@/components/ElectricBorder"
4. No external dependencies required - pure React + Canvas
================================================================================
QUICK CUSTOMIZATION (via props)
================================================================================
| Prop | Type | Default | Description |
|--------------|---------------------|-------------|------------------------------------|
| width | number | 354 | Width of the container in pixels |
| height | number | 504 | Height of the container in pixels |
| borderRadius | number | 24 | Corner radius in pixels |
| color | string (hex) | "#4A9FFF" | Electric border color |
| lineWidth | number | 1 | Thickness of the electric line |
| speed | number | 1.5 | Animation speed multiplier |
| displacement | number | 60 | How far the electric effect extends|
| className | string | "" | Additional CSS classes |
| children | React.ReactNode | undefined | Content to display inside the card |
================================================================================
COMMON MODIFICATIONS
================================================================================
1. BLUE THEME (default):
<ElectricBorder color="#4A9FFF" />
2. GREEN THEME:
<ElectricBorder color="#00ff88" />
3. ORANGE THEME:
<ElectricBorder color="#DD8448" />
4. PURPLE THEME:
<ElectricBorder color="#BD93F9" />
5. SMALLER CARD:
<ElectricBorder width={280} height={400} displacement={50} />
6. FASTER ANIMATION:
<ElectricBorder speed={2.5} />
7. SUBTLE EFFECT:
<ElectricBorder displacement={30} speed={0.8} />
8. DRAMATIC EFFECT:
<ElectricBorder displacement={80} speed={2} />
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useRef, useEffect, useCallback } from "react";
export interface ElectricBorderProps {
width?: number;
height?: number;
borderRadius?: number;
color?: string;
lineWidth?: number;
speed?: number;
displacement?: number;
className?: string;
children?: React.ReactNode;
}
export default function ElectricBorder({
width = 354,
height = 504,
borderRadius = 24,
color = "#4A9FFF",
lineWidth = 1,
speed = 1.5,
displacement = 60,
className = "",
children,
}: ElectricBorderProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const animationRef = useRef<number | undefined>(undefined);
const stateRef = useRef({
time: 0,
lastFrameTime: 0,
});
const random = useCallback((x: number) => {
return (Math.sin(x * 12.9898) * 43758.5453) % 1;
}, []);
const noise2D = useCallback((x: number, y: number) => {
const i = Math.floor(x);
const j = Math.floor(y);
const fx = x - i;
const fy = y - j;
const a = random(i + j * 57);
const b = random(i + 1 + j * 57);
const c = random(i + (j + 1) * 57);
const d = random(i + 1 + (j + 1) * 57);
const ux = fx * fx * (3.0 - 2.0 * fx);
const uy = fy * fy * (3.0 - 2.0 * fy);
return (
a * (1 - ux) * (1 - uy) +
b * ux * (1 - uy) +
c * (1 - ux) * uy +
d * ux * uy
);
}, [random]);
const octavedNoise = useCallback((
x: number,
octaves: number,
lacunarity: number,
gain: number,
baseAmplitude: number,
baseFrequency: number,
time: number = 0,
seed: number = 0,
baseFlatness: number = 1.0
) => {
let y = 0;
let amplitude = baseAmplitude;
let frequency = baseFrequency;
for (let i = 0; i < octaves; i++) {
let octaveAmplitude = amplitude;
if (i === 0) {
octaveAmplitude *= baseFlatness;
}
y += octaveAmplitude * noise2D(frequency * x + seed * 100, time * frequency * 0.3);
frequency *= lacunarity;
amplitude *= gain;
}
return y;
}, [noise2D]);
const getCornerPoint = useCallback((
centerX: number,
centerY: number,
radius: number,
startAngle: number,
arcLength: number,
progress: number
) => {
const angle = startAngle + progress * arcLength;
return {
x: centerX + radius * Math.cos(angle),
y: centerY + radius * Math.sin(angle),
};
}, []);
const getRoundedRectPoint = useCallback((
t: number,
left: number,
top: number,
rectWidth: number,
rectHeight: number,
radius: number
) => {
const straightWidth = rectWidth - 2 * radius;
const straightHeight = rectHeight - 2 * radius;
const cornerArc = (Math.PI * radius) / 2;
const totalPerimeter = 2 * straightWidth + 2 * straightHeight + 4 * cornerArc;
const distance = t * totalPerimeter;
let accumulated = 0;
if (distance <= accumulated + straightWidth) {
const progress = (distance - accumulated) / straightWidth;
return { x: left + radius + progress * straightWidth, y: top };
}
accumulated += straightWidth;
if (distance <= accumulated + cornerArc) {
const progress = (distance - accumulated) / cornerArc;
return getCornerPoint(left + rectWidth - radius, top + radius, radius, -Math.PI / 2, Math.PI / 2, progress);
}
accumulated += cornerArc;
if (distance <= accumulated + straightHeight) {
const progress = (distance - accumulated) / straightHeight;
return { x: left + rectWidth, y: top + radius + progress * straightHeight };
}
accumulated += straightHeight;
if (distance <= accumulated + cornerArc) {
const progress = (distance - accumulated) / cornerArc;
return getCornerPoint(left + rectWidth - radius, top + rectHeight - radius, radius, 0, Math.PI / 2, progress);
}
accumulated += cornerArc;
if (distance <= accumulated + straightWidth) {
const progress = (distance - accumulated) / straightWidth;
return { x: left + rectWidth - radius - progress * straightWidth, y: top + rectHeight };
}
accumulated += straightWidth;
if (distance <= accumulated + cornerArc) {
const progress = (distance - accumulated) / cornerArc;
return getCornerPoint(left + radius, top + rectHeight - radius, radius, Math.PI / 2, Math.PI / 2, progress);
}
accumulated += cornerArc;
if (distance <= accumulated + straightHeight) {
const progress = (distance - accumulated) / straightHeight;
return { x: left, y: top + rectHeight - radius - progress * straightHeight };
}
accumulated += straightHeight;
const progress = (distance - accumulated) / cornerArc;
return getCornerPoint(left + radius, top + radius, radius, Math.PI, Math.PI / 2, progress);
}, [getCornerPoint]);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const state = stateRef.current;
const canvasWidth = width + displacement * 2;
const canvasHeight = height + displacement * 2;
const borderOffset = displacement;
const octaves = 10;
const lacunarity = 1.6;
const gain = 0.7;
const amplitude = 0.075;
const frequency = 10;
const baseFlatness = 0;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const drawElectricBorder = (currentTime: number) => {
const deltaTime = (currentTime - state.lastFrameTime) / 1000;
state.time += deltaTime * speed;
state.lastFrameTime = currentTime;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.lineCap = "round";
ctx.lineJoin = "round";
const scale = displacement;
const left = borderOffset;
const top = borderOffset;
const borderWidth = canvasWidth - 2 * borderOffset;
const borderHeight = canvasHeight - 2 * borderOffset;
const maxRadius = Math.min(borderWidth, borderHeight) / 2;
const radius = Math.min(borderRadius, maxRadius);
const approximatePerimeter = 2 * (borderWidth + borderHeight) + 2 * Math.PI * radius;
const sampleCount = Math.floor(approximatePerimeter / 2);
ctx.beginPath();
for (let i = 0; i <= sampleCount; i++) {
const progress = i / sampleCount;
const point = getRoundedRectPoint(progress, left, top, borderWidth, borderHeight, radius);
const xNoise = octavedNoise(progress * 8, octaves, lacunarity, gain, amplitude, frequency, state.time, 0, baseFlatness);
const yNoise = octavedNoise(progress * 8, octaves, lacunarity, gain, amplitude, frequency, state.time, 1, baseFlatness);
const displacedX = point.x + xNoise * scale;
const displacedY = point.y + yNoise * scale;
if (i === 0) {
ctx.moveTo(displacedX, displacedY);
} else {
ctx.lineTo(displacedX, displacedY);
}
}
ctx.closePath();
ctx.stroke();
animationRef.current = requestAnimationFrame(drawElectricBorder);
};
animationRef.current = requestAnimationFrame(drawElectricBorder);
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [width, height, borderRadius, color, lineWidth, speed, displacement, octavedNoise, getRoundedRectPoint]);
const gradientColor = `oklch(from ${color} 0.3 calc(c / 2) h / 0.4)`;
return (
<div
className={`relative ${className}`}
style={{
padding: "2px",
borderRadius: `${borderRadius}px`,
background: `linear-gradient(-30deg, ${gradientColor}, transparent, ${gradientColor}), linear-gradient(to bottom, oklch(0.185 0 0), oklch(0.185 0 0))`,
}}
>
<div className="relative">
<div
style={{
position: "relative",
width: `${width}px`,
height: `${height}px`,
}}
>
<canvas
ref={canvasRef}
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: `${width + displacement * 2}px`,
height: `${height + displacement * 2}px`,
pointerEvents: "none",
}}
/>
</div>
{/* Glow layer 1 */}
<div
style={{
border: `2px solid ${color}99`,
borderRadius: `${borderRadius}px`,
width: "100%",
height: "100%",
position: "absolute",
top: 0,
left: 0,
filter: "blur(1px)",
pointerEvents: "none",
}}
/>
{/* Glow layer 2 */}
<div
style={{
border: `2px solid ${color}`,
borderRadius: `${borderRadius}px`,
width: "100%",
height: "100%",
position: "absolute",
top: 0,
left: 0,
filter: "blur(4px)",
pointerEvents: "none",
}}
/>
</div>
{/* Overlay 1 */}
<div
style={{
position: "absolute",
width: "100%",
height: "100%",
top: 0,
left: 0,
borderRadius: `${borderRadius}px`,
opacity: 1,
mixBlendMode: "overlay",
transform: "scale(1.1)",
filter: "blur(16px)",
background: "linear-gradient(-30deg, white, transparent 30%, transparent 70%, white)",
pointerEvents: "none",
}}
/>
{/* Overlay 2 */}
<div
style={{
position: "absolute",
width: "100%",
height: "100%",
top: 0,
left: 0,
borderRadius: `${borderRadius}px`,
opacity: 0.5,
mixBlendMode: "overlay",
transform: "scale(1.1)",
filter: "blur(16px)",
background: "linear-gradient(-30deg, white, transparent 30%, transparent 70%, white)",
pointerEvents: "none",
}}
/>
{/* Background glow */}
<div
style={{
position: "absolute",
width: "100%",
height: "100%",
top: 0,
left: 0,
borderRadius: `${borderRadius}px`,
filter: "blur(32px)",
transform: "scale(1.1)",
opacity: 0.3,
zIndex: -1,
background: `linear-gradient(-30deg, ${color}, transparent, ${color})`,
pointerEvents: "none",
}}
/>
{/* Content container */}
{children && (
<div
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
}}
>
{children}
</div>
)}
</div>
);
}
export { ElectricBorder };import ElectricBorder from "@/components/ElectricBorder";
// Basic usage with blue theme (default)
export default function ElectricCard() {
return (
<div className="bg-[#0a0a0a] min-h-screen flex items-center justify-center">
<ElectricBorder
width={354}
height={504}
color="#4A9FFF"
borderRadius={24}
speed={1.5}
displacement={60}
>
<div className="flex flex-col h-full p-12 text-white">
<h2 className="text-3xl font-bold">Electric Border</h2>
<p className="mt-auto opacity-50">Dramatic emphasis effect</p>
</div>
</ElectricBorder>
</div>
);
}
// Green theme
export function GreenElectric() {
return (
<div className="bg-[#0a0a0a] min-h-screen flex items-center justify-center">
<ElectricBorder color="#00ff88" speed={2}>
<div className="p-12 text-white">
<h2 className="text-3xl">Matrix Vibes</h2>
</div>
</ElectricBorder>
</div>
);
}
// Orange theme (original)
export function OrangeElectric() {
return (
<div className="bg-[#0a0a0a] min-h-screen flex items-center justify-center">
<ElectricBorder color="#DD8448">
<div className="p-12 text-white">
<h2 className="text-3xl">Warm Electric</h2>
</div>
</ElectricBorder>
</div>
);
}
// Smaller subtle card
export function SubtleCard() {
return (
<div className="bg-[#0a0a0a] p-8">
<ElectricBorder
width={200}
height={280}
displacement={30}
speed={0.8}
color="#BD93F9"
>
<div className="p-6 text-white text-center">
<p className="text-lg">Subtle</p>
</div>
</ElectricBorder>
</div>
);
}