Docs
Spotlight Card

Spotlight Card

An interactive card component with dynamic spotlight effects that follow cursor movement.

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:

PropTypeDefaultDescription
childrenReact.ReactNodeRequiredThe content to be rendered inside the card
spotlightSizenumber250Size of the spotlight effect in pixels
spotlightOpacitynumber0.15Opacity of the spotlight effect (0-1)
gradientColorstringrgb(168, 85, 247)Color of the spotlight gradient in dark mode
lightGradientColorstringundefinedOptional color override for light mode
glowEffectbooleanfalseEnables a subtle glow effect
multiSpotlightbooleanfalseEnables dual spotlight effect
spotlightBlurbooleanfalseMakes the spotlight more diffused
glowSizenumber100Size of the glow effect in pixels
glowOpacitynumber0.15Opacity of the glow effect (0-1)
animatedbooleanfalseEnables animated spotlight movement
disableScalebooleanfalseDisables the hover scale effect
initialHoveredbooleanfalseWhether 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>