Docs
Artistic Cursor Effects

Artistic Cursor Effects

Beautiful and interactive cursor effects with smooth animations and transitions

Move Your Cursor Here

Glow Effect

Installation

Install the following dependencies in your project:

npm install framer-motion

Copy and paste the following code into your project.

components/ui/artistic-cursor-effects.tsx

"use client";
 
import React, { useEffect, useRef, useState } from "react";
import { motion, useSpring, useMotionValue } from "framer-motion";
 
interface ArtisticCursorEffectsProps {
  variant?: "glow" | "trail" | "magnetic";
  color?: string;
  size?: number;
  trailLength?: number;
  magneticStrength?: number;
  glowIntensity?: number;
  children?: React.ReactNode;
}
 
export function ArtisticCursorEffects({
  variant = "glow",
  color = "#6366f1",
  size = 20,
  trailLength = 8,
  magneticStrength = 0.5,
  glowIntensity = 1,
  children,
}: ArtisticCursorEffectsProps) {
  const cursorRef = useRef<HTMLDivElement>(null);
  const [cursorVisible, setCursorVisible] = useState(false);
  const mouseX = useMotionValue(0);
  const mouseY = useMotionValue(0);
  const scale = useMotionValue(1);
 
  const springConfig = { damping: 25, stiffness: 250 };
  const springX = useSpring(mouseX, springConfig);
  const springY = useSpring(mouseY, springConfig);
  const springScale = useSpring(scale, { damping: 15, stiffness: 150 });
 
  const [trails] = useState(() =>
    Array.from({ length: trailLength }, (_, i) => ({
      delay: i * 0.05,
      opacity: 1 - i / trailLength,
      scale: 1 - i * 0.05,
    })),
  );
 
  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      const x = e.clientX - size / 2;
      const y = e.clientY - size / 2;
      mouseX.set(x);
      mouseY.set(y);
      setCursorVisible(true);
    };
 
    const handleMouseLeave = () => {
      setCursorVisible(false);
    };
 
    const handleMouseDown = () => {
      scale.set(0.9);
    };
 
    const handleMouseUp = () => {
      scale.set(1);
    };
 
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("mouseleave", handleMouseLeave);
    window.addEventListener("mousedown", handleMouseDown);
    window.addEventListener("mouseup", handleMouseUp);
 
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseleave", handleMouseLeave);
      window.removeEventListener("mousedown", handleMouseDown);
      window.removeEventListener("mouseup", handleMouseUp);
    };
  }, [mouseX, mouseY, scale, size]);
 
  const renderCursorEffect = () => {
    switch (variant) {
      case "trail":
        return (
          <>
            {trails.map((trail, index) => (
              <motion.div
                key={index}
                className="pointer-events-none absolute rounded-full"
                style={{
                  width: size,
                  height: size,
                  x: springX,
                  y: springY,
                  backgroundColor: color,
                  opacity: trail.opacity,
                  scale: springScale.get() * trail.scale,
                }}
              />
            ))}
            <motion.div
              className="pointer-events-none absolute rounded-full border-2"
              style={{
                width: size,
                height: size,
                x: springX,
                y: springY,
                borderColor: color,
                scale: springScale,
              }}
              animate={{ rotate: 360 }}
              transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
            />
          </>
        );
 
      case "magnetic":
        return (
          <>
            <motion.div
              className="pointer-events-none absolute rounded-full"
              style={{
                width: size * 3,
                height: size * 3,
                x: springX,
                y: springY,
                scale: springScale,
              }}
              animate={{
                backgroundColor: `${color}10`,
                scale: [1, 1.2, 1],
                rotate: [0, 180, 360],
              }}
              transition={{
                duration: 3,
                ease: "easeInOut",
                repeat: Infinity,
                repeatType: "reverse",
              }}
            />
            <motion.div
              className="pointer-events-none absolute rounded-full"
              style={{
                width: size * 2,
                height: size * 2,
                x: springX,
                y: springY,
                scale: springScale,
              }}
              animate={{
                backgroundColor: `${color}20`,
                scale: [1.1, 0.9, 1.1],
                rotate: [180, 360, 540],
              }}
              transition={{
                duration: 2,
                ease: "easeInOut",
                repeat: Infinity,
                repeatType: "reverse",
              }}
            />
            <motion.div
              className="pointer-events-none absolute rounded-full"
              style={{
                width: size,
                height: size,
                x: springX,
                y: springY,
                backgroundColor: color,
                scale: springScale,
              }}
            />
          </>
        );
 
      case "glow":
      default:
        return (
          <>
            <motion.div
              className="pointer-events-none absolute rounded-full"
              style={{
                width: size * 2,
                height: size * 2,
                x: springX,
                y: springY,
                scale: springScale,
              }}
              animate={{
                backgroundColor: `${color}20`,
                scale: [1, 1.5, 1],
              }}
              transition={{
                duration: 2,
                ease: "easeInOut",
                repeat: Infinity,
              }}
            />
            <motion.div
              className="pointer-events-none absolute rounded-full"
              style={{
                width: size,
                height: size,
                x: springX,
                y: springY,
                backgroundColor: color,
                scale: springScale,
              }}
              animate={{
                boxShadow: [
                  `0 0 ${20 * glowIntensity}px ${10 * glowIntensity}px ${color}50`,
                  `0 0 ${30 * glowIntensity}px ${15 * glowIntensity}px ${color}30`,
                  `0 0 ${20 * glowIntensity}px ${10 * glowIntensity}px ${color}50`,
                ],
              }}
              transition={{
                duration: 1.5,
                ease: "easeInOut",
                repeat: Infinity,
              }}
            />
          </>
        );
    }
  };
 
  return (
    <div ref={cursorRef} className="relative">
      <motion.div
        className="pointer-events-none fixed left-0 top-0 z-50 h-screen w-screen"
        initial={{ opacity: 0 }}
        animate={{ opacity: cursorVisible ? 1 : 0 }}
        transition={{ duration: 0.2 }}
      >
        {renderCursorEffect()}
      </motion.div>
      {children}
    </div>
  );
}

Update the import paths to match your project setup.

import { ArtisticCursorEffects } from "@/components/ui/artistic-cursor-effects";

Props

ArtisticCursorEffects

PropTypeDefaultDescription
variant'glow' | 'trail' | 'magnetic''glow'The type of cursor effect to display
colorstring'#6366f1'Color of the cursor effect
sizenumber20Size of the cursor in pixels
trailLengthnumber8Number of trail elements (for trail variant)
magneticStrengthnumber0.5Strength of magnetic effect
glowIntensitynumber1Intensity of the glow effect
childrenReact.ReactNode-Content to apply the cursor effect to

Examples

Basic Glow Effect

import { ArtisticCursorEffects } from "@/components/ui/artistic-cursor-effects";
 
export default function GlowExample() {
  return (
    <ArtisticCursorEffects
      variant="glow"
      color="#f43f5e"
      size={25}
      glowIntensity={1.5}
    >
      <div className="min-h-[400px] flex items-center justify-center">
        <h1 className="text-3xl font-bold">Hover over me!</h1>
      </div>
    </ArtisticCursorEffects>
  );
}

Interactive Trail Effect

export default function TrailExample() {
  const [intensity, setIntensity] = useState(1);
 
  return (
    <ArtisticCursorEffects
      variant="trail"
      color="#10b981"
      size={20}
      trailLength={Math.floor(intensity * 8)}
    >
      <div className="space-y-4">
        <Slider
          value={[intensity]}
          onValueChange={([value]) => setIntensity(value)}
          min={0.5}
          max={2}
          step={0.1}
        />
        <div className="text-center">
          Trail Length: {Math.floor(intensity * 8)}
        </div>
      </div>
    </ArtisticCursorEffects>
  );
}

Magnetic Effect with Dark Mode

export default function MagneticExample() {
  return (
    <ArtisticCursorEffects
      variant="magnetic"
      color="#f59e0b"
      size={30}
      magneticStrength={0.8}
    >
      <div className="grid gap-4 p-8">
        <div className="rounded-lg bg-white p-6 dark:bg-gray-800">
          <h2 className="text-2xl font-bold">Light Mode Content</h2>
        </div>
        <div className="rounded-lg bg-gray-800 p-6 dark:bg-white">
          <h2 className="text-2xl font-bold text-white dark:text-gray-800">
            Dark Mode Content
          </h2>
        </div>
      </div>
    </ArtisticCursorEffects>
  );
}

Custom Color Transitions

export default function ColorTransitionExample() {
  const colors = ["#6366f1", "#f43f5e", "#10b981", "#f59e0b"];
  const [colorIndex, setColorIndex] = useState(0);
 
  useEffect(() => {
    const interval = setInterval(() => {
      setColorIndex((prev) => (prev + 1) % colors.length);
    }, 2000);
    return () => clearInterval(interval);
  }, []);
 
  return (
    <ArtisticCursorEffects
      variant="glow"
      color={colors[colorIndex]}
      size={25}
      glowIntensity={1.2}
    >
      <div className="flex h-[300px] items-center justify-center">
        <div
          className="h-32 w-32 rounded-full transition-colors duration-1000"
          style={{ backgroundColor: colors[colorIndex] }}
        />
      </div>
    </ArtisticCursorEffects>
  );
}