Cursor Spotlight

A darkened section where only the area near your cursor is revealed — like sweeping a flashlight across the page

cursorspotlightflashlightrevealinteractive
Install dependencies
$npm install framer-motion
Preview

Source Code

"use client";

import { useRef } from "react";
import {
  motion,
  useMotionTemplate,
  useMotionValue,
  useReducedMotion,
  useSpring,
} from "framer-motion";

interface CursorSpotlightProps {
  children?: React.ReactNode;
  size?: number;
  overlayColor?: string;
  className?: string;
}

export default function CursorSpotlight({
  children,
  size = 180,
  overlayColor = "rgba(5, 6, 12, 0.93)",
  className = "",
}: CursorSpotlightProps) {
  const ref = useRef<HTMLDivElement>(null);
  const reduceMotion = useReducedMotion();

  const x = useMotionValue(0);
  const y = useMotionValue(0);
  const radius = useMotionValue(0);

  const springConfig = reduceMotion
    ? { stiffness: 2000, damping: 90 }
    : { stiffness: 150, damping: 20 };

  const sx = useSpring(x, springConfig);
  const sy = useSpring(y, springConfig);
  const sr = useSpring(radius, { stiffness: 200, damping: 25 });

  const overlay = useMotionTemplate`radial-gradient(circle ${sr}px at ${sx}px ${sy}px, transparent 30%, ${overlayColor} 100%)`;

  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    x.set(e.clientX - rect.left);
    y.set(e.clientY - rect.top);
    radius.set(size);
  };

  return (
    <div
      ref={ref}
      onMouseMove={handleMouseMove}
      onMouseLeave={() => radius.set(0)}
      className={`relative overflow-hidden bg-[#05060c] ${className}`}
    >
      {children}
      <motion.div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 z-10"
        style={{ background: overlay }}
      />
    </div>
  );
}