Basic Spotlight
Default Configuration
Experience the elegant spotlight effect that smoothly follows your cursor movement, creating an engaging interactive element that brings your UI to life.
Multi-spotlight with Glow
Enhanced Effects
Dual spotlight effect combined with a subtle glow creates depth and dimension, making your interface more dynamic and visually appealing.
Animated Spotlight
Interactive Animation
Watch as the spotlight comes alive with smooth animations, creating an immersive experience that responds to user interaction in real-time.
Custom Configuration
Advanced Settings
Fully customizable spotlight with precise control over size, opacity, and glow effects to match your design requirements perfectly.
Installation
Copy and paste the following code into your project.
"use client";
import React, { useRef, useState, useEffect } from "react";
import { cn } from "@/lib/utils";
import { useTheme } from "next-themes";
export interface SpotlightCardProps
extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
spotlightSize?: number;
spotlightOpacity?: number;
gradientColor?: string;
lightGradientColor?: string;
glowEffect?: boolean;
multiSpotlight?: boolean;
spotlightBlur?: boolean;
glowSize?: number;
glowOpacity?: number;
animated?: boolean;
initialHovered?: boolean;
disableScale?: boolean;
}
const SpotlightCard = ({
children,
className,
spotlightSize = 250,
spotlightOpacity = 0.15,
gradientColor = "rgb(168, 85, 247)",
lightGradientColor,
glowEffect = false,
multiSpotlight = false,
spotlightBlur = false,
glowSize = 100,
glowOpacity = 0.15,
animated = false,
initialHovered = false,
disableScale = false,
...props
}: SpotlightCardProps) => {
const divRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<{ x: number; y: number }>({
x: 0,
y: 0,
});
const [secondaryPosition, setSecondaryPosition] = useState<{
x: number;
y: number;
}>({
x: 0,
y: 0,
});
const [opacity, setOpacity] = useState(initialHovered ? 1 : 0);
const [hovered, setHovered] = useState(initialHovered);
const { theme } = useTheme();
useEffect(() => {
if (animated && hovered) {
const interval = setInterval(() => {
setSecondaryPosition({
x: Math.random() * (divRef.current?.offsetWidth || 0),
y: Math.random() * (divRef.current?.offsetHeight || 0),
});
}, 1000);
return () => clearInterval(interval);
}
}, [animated, hovered]);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!divRef.current) return;
const div = divRef.current;
const rect = div.getBoundingClientRect();
const newPosition = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
};
setPosition(newPosition);
if (multiSpotlight) {
setSecondaryPosition({
x: div.offsetWidth - (e.clientX - rect.left),
y: div.offsetHeight - (e.clientY - rect.top),
});
}
};
const handleMouseEnter = () => {
setOpacity(1);
setHovered(true);
};
const handleMouseLeave = () => {
setOpacity(0);
setHovered(false);
};
const getSpotlightBackground = (
pos: { x: number; y: number },
size: number,
blur = false,
) => {
return `radial-gradient(${size}px ${blur ? "ellipse" : "circle"} at ${
pos.x
}px ${pos.y}px,
var(--spotlight-color) 0%,
transparent ${blur ? "75%" : "65%"})`;
};
// Determine which gradient color to use based on the current theme
const currentGradientColor =
theme === "light" && lightGradientColor
? lightGradientColor
: gradientColor;
return (
<div
ref={(node) => {
divRef.current = node;
}}
className={cn(
"relative w-full overflow-hidden rounded-xl border border-border/40 bg-background transition-transform duration-300",
hovered && !disableScale && "scale-[1.02]",
className,
)}
onMouseMove={handleMouseMove}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
{...props}
>
{/* Main Spotlight */}
<div
className="pointer-events-none absolute -inset-px opacity-0 transition-opacity duration-300"
style={
{
opacity,
background: getSpotlightBackground(
position,
spotlightSize,
spotlightBlur,
),
["--spotlight-color" as string]: `color-mix(in srgb, var(--gradient-color, ${currentGradientColor}), transparent ${
(1 - spotlightOpacity) * 100
}%)`,
} as React.CSSProperties
}
/>
{/* Secondary Spotlight */}
{multiSpotlight && (
<div
className="pointer-events-none absolute -inset-px opacity-0 transition-opacity duration-300"
style={
{
opacity: opacity * 0.7,
background: getSpotlightBackground(
secondaryPosition,
spotlightSize * 0.8,
spotlightBlur,
),
["--spotlight-color" as string]: `color-mix(in srgb, var(--gradient-color, ${currentGradientColor}), transparent ${
(1 - spotlightOpacity * 0.8) * 100
}%)`,
} as React.CSSProperties
}
/>
)}
{/* Glow Effect */}
{glowEffect && (
<div
className="pointer-events-none absolute -inset-px opacity-0 blur-xl transition-opacity duration-300"
style={
{
opacity: opacity * glowOpacity,
background: getSpotlightBackground(position, glowSize),
["--spotlight-color" as string]: `color-mix(in srgb, var(--gradient-color, ${currentGradientColor}), transparent 15%)`,
} as React.CSSProperties
}
/>
)}
<div className="relative">{children}</div>
</div>
);
};
export SpotlightCard;
Update the import paths to match your project setup.
import SpotlightCard from "@/components/ui/spotlight-card";
Usage
import SpotlightCard from "@/components/ui/spotlight-card";
export default function Example() {
return (
<SpotlightCard>
<h3>Your Content Here</h3>
<p>Add any content inside the card.</p>
</SpotlightCard>
);
}
Props
Here are the available props for customizing the SpotlightCard component:
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | Required | The content to be rendered inside the card |
spotlightSize | number | 250 | Size of the spotlight effect in pixels |
spotlightOpacity | number | 0.15 | Opacity of the spotlight effect (0-1) |
gradientColor | string | rgb(168, 85, 247) | Color of the spotlight gradient in dark mode |
lightGradientColor | string | undefined | Optional color override for light mode |
glowEffect | boolean | false | Enables a subtle glow effect |
multiSpotlight | boolean | false | Enables dual spotlight effect |
spotlightBlur | boolean | false | Makes the spotlight more diffused |
glowSize | number | 100 | Size of the glow effect in pixels |
glowOpacity | number | 0.15 | Opacity of the glow effect (0-1) |
animated | boolean | false | Enables animated spotlight movement |
disableScale | boolean | false | Disables the hover scale effect |
initialHovered | boolean | false | Whether the spotlight should be visible by default |
Examples
Basic Card
<SpotlightCard>
<YourContent />
</SpotlightCard>
Interactive Multi-spotlight
<SpotlightCard
multiSpotlight
glowEffect
gradientColor="rgb(99, 102, 241)"
lightGradientColor="rgb(79, 82, 221)"
>
<YourContent />
</SpotlightCard>
Animated with Blur
<SpotlightCard
animated
spotlightBlur
glowEffect
spotlightSize={300}
spotlightOpacity={0.2}
glowSize={200}
glowOpacity={0.3}
gradientColor="rgb(244, 63, 94)"
>
<YourContent />
</SpotlightCard>
Custom Theme-Aware
<SpotlightCard
spotlightSize={300}
spotlightOpacity={0.2}
glowEffect
glowSize={150}
glowOpacity={0.2}
gradientColor="rgb(34, 197, 94)"
lightGradientColor="rgb(22, 163, 74)"
spotlightBlur
>
<YourContent />
</SpotlightCard>