Command Palette

Search for a command to run...

Spotlight

Spotlight cards with a mouse-sensitive spotlight effect that highlights the border of the div or section as you move your cursor

Installation

Install the following dependencies:

pnpm add motion

Create a file at components/motion/spotlight.tsx and paste the given code.

"use client";
import React, { useRef, useState, useCallback, useEffect } from "react";
import { motion, useSpring, useTransform, SpringOptions } from "motion/react";
import { cn } from "@/lib/utils";
 
export type SpotlightProps = {
    className?: string;
    size?: number;
    springOptions?: SpringOptions;
};
 
export function Spotlight({
    className,
    size = 200,
    springOptions = { bounce: 0 },
}: SpotlightProps) {
    const containerRef = useRef<HTMLDivElement>(null);
    const [isHovered, setIsHovered] = useState(false);
    const [parentElement, setParentElement] = useState<HTMLElement | null>(
        null,
    );
 
    const mouseX = useSpring(0, springOptions);
    const mouseY = useSpring(0, springOptions);
 
    const spotlightLeft = useTransform(mouseX, (x) => `${x - size / 2}px`);
    const spotlightTop = useTransform(mouseY, (y) => `${y - size / 2}px`);
 
    useEffect(() => {
        if (containerRef.current) {
            const parent = containerRef.current.parentElement;
            if (parent) {
                parent.style.position = "relative";
                parent.style.overflow = "hidden";
                setParentElement(parent);
            }
        }
    }, []);
 
    const handleMouseMove = useCallback(
        (event: MouseEvent) => {
            if (!parentElement) return;
            const { left, top } = parentElement.getBoundingClientRect();
            mouseX.set(event.clientX - left);
            mouseY.set(event.clientY - top);
        },
        [mouseX, mouseY, parentElement],
    );
 
    useEffect(() => {
        if (!parentElement) return;
 
        parentElement.addEventListener("mousemove", handleMouseMove);
        parentElement.addEventListener("mouseenter", () => setIsHovered(true));
        parentElement.addEventListener("mouseleave", () => setIsHovered(false));
 
        return () => {
            parentElement.removeEventListener("mousemove", handleMouseMove);
            parentElement.removeEventListener("mouseenter", () =>
                setIsHovered(true),
            );
            parentElement.removeEventListener("mouseleave", () =>
                setIsHovered(false),
            );
        };
    }, [parentElement, handleMouseMove]);
 
    return (
        <motion.div
            ref={containerRef}
            className={cn(
                "pointer-events-none absolute rounded-full bg-[radial-gradient(circle_at_center,var(--tw-gradient-stops),transparent_80%)] blur-xl transition-opacity duration-200",
                "from-zinc-50 via-zinc-100 to-zinc-200",
                isHovered ? "opacity-100" : "opacity-0",
                className,
            )}
            style={{
                width: size,
                height: size,
                left: spotlightLeft,
                top: spotlightTop,
            }}
        />
    );
}

Update the import paths to match your project setup.