Docs
Spotlight

Spotlight

A dynamic spotlight effect component with layered animations.

Spotlight

Installation

Copy and paste the following code into your project.

components/ui/spotlight.tsx

import React, { useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { cn } from "@/lib/utils";
 
export interface SpotlightProps extends React.HTMLAttributes<HTMLDivElement> {
  children?: React.ReactNode;
  spotlightColor?: string;
  spotlightIntensity?: number;
  enableAnimations?: boolean;
}
 
export function Spotlight({
  children,
  className,
  spotlightColor = "#4332CF",
  spotlightIntensity = 80,
  enableAnimations = true,
  ...props
}: SpotlightProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const normalizedIntensity = spotlightIntensity / 100;
 
  const baseVariants = {
    initial: { opacity: 0, scale: 0.8 },
    animate: {
      opacity: 1,
      scale: 1,
      transition: {
        duration: 1.5,
        ease: "easeOut",
      },
    },
    pulse: {
      opacity: [
        normalizedIntensity * 0.3,
        normalizedIntensity * 0.4,
        normalizedIntensity * 0.3,
      ],
      scale: [1, 1.1, 1],
      transition: {
        duration: 8,
        repeat: Infinity,
        repeatType: "reverse" as const,
        ease: "easeInOut",
      },
    },
  };
 
  const middleVariants = {
    initial: { opacity: 0, scale: 0.9 },
    animate: {
      opacity: 1,
      scale: 1,
      transition: {
        duration: 1.5,
        ease: "easeOut",
      },
    },
    pulse: {
      opacity: [
        normalizedIntensity * 0.6,
        normalizedIntensity * 0.7,
        normalizedIntensity * 0.6,
      ],
      scale: [1, 1.05, 1],
      transition: {
        duration: 6,
        repeat: Infinity,
        repeatType: "reverse" as const,
        ease: "easeInOut",
        delay: 0.5,
      },
    },
  };
 
  const mainVariants = {
    initial: { opacity: 0, scale: 0.95 },
    animate: {
      opacity: 1,
      scale: 1,
      transition: {
        duration: 1.5,
        ease: "easeOut",
      },
    },
    pulse: {
      opacity: [
        normalizedIntensity,
        normalizedIntensity * 1.1,
        normalizedIntensity,
      ],
      scale: [1, 1.02, 1],
      transition: {
        duration: 4,
        repeat: Infinity,
        repeatType: "reverse" as const,
        ease: "easeInOut",
        delay: 1,
      },
    },
  };
 
  return (
    <div
      ref={containerRef}
      className={cn("relative overflow-hidden bg-black", className)}
      {...props}
    >
      <AnimatePresence>
        <motion.div
          key="base-layer"
          className="z-8 absolute inset-0 w-full"
          initial="initial"
          animate={enableAnimations ? "pulse" : "animate"}
          variants={baseVariants}
          style={{
            background: `radial-gradient(ellipse at 50% 100%, 
              ${spotlightColor} 0%,
              rgba(67, 50, 207, 0.4) 25%,
              rgba(67, 50, 207, 0.1) 50%,
              transparent 80%)`,
            mixBlendMode: "screen",
          }}
        />
 
        <motion.div
          key="middle-layer"
          className="z-9 absolute inset-0 w-full"
          initial="initial"
          animate={enableAnimations ? "pulse" : "animate"}
          variants={middleVariants}
          style={{
            background: `radial-gradient(ellipse at 50% 100%, 
              ${spotlightColor} 0%,
              rgba(67, 50, 207, 0.6) 20%,
              rgba(67, 50, 207, 0.2) 40%,
              transparent 70%)`,
            mixBlendMode: "screen",
          }}
        />
 
        <motion.div
          key="main-layer"
          className="absolute inset-0 z-10 w-full"
          initial="initial"
          animate={enableAnimations ? "pulse" : "animate"}
          variants={mainVariants}
          style={{
            background: `radial-gradient(ellipse at 50% 100%, 
              ${spotlightColor} 0%, 
              rgba(67, 50, 207, 0.8) 15%,
              rgba(67, 50, 207, 0.3) 35%,
              transparent 70%)`,
            mixBlendMode: "screen",
          }}
        />
        <div key="content" className="relative z-20">
          {children}
        </div>
      </AnimatePresence>
    </div>
  );
}

Update the import paths to match your project setup.

import Spotlight from "@/components/ui/spotlight";

Usage

import { Spotlight } from "@/components/ui/spotlight";
 
export default function SpotlightDemo() {
  return (
    <Spotlight>
      <h1>Your content here</h1>
    </Spotlight>
  );
}

Examples

Basic spotlight

A basic spotlight effect with default settings.

<Spotlight>
  <h1>Spotlight</h1>
</Spotlight>

Custom color and intensity

Customize the spotlight's color and intensity.

<Spotlight spotlightColor="#4332CF" spotlightIntensity={80}>
  <h1>Custom Spotlight</h1>
</Spotlight>

Disabled animations

Use the spotlight effect without animations.

<Spotlight enableAnimations={false}>
  <h1>Static Spotlight</h1>
</Spotlight>

Props

PropTypeDefaultDescription
spotlightColorstring"#4332CF"The color of the spotlight effect
spotlightIntensitynumber100The intensity of the spotlight effect (0-100)
enableAnimationsbooleantrueWhether to enable the pulsing animations
classNamestring""Additonal CSS classes to apply to the spotlight
childrenReactNodeundefinedThe content to display on top of the spotlight