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
Prop | Type | Default | Description |
---|---|---|---|
variant | 'glow' | 'trail' | 'magnetic' | 'glow' | The type of cursor effect to display |
color | string | '#6366f1' | Color of the cursor effect |
size | number | 20 | Size of the cursor in pixels |
trailLength | number | 8 | Number of trail elements (for trail variant) |
magneticStrength | number | 0.5 | Strength of magnetic effect |
glowIntensity | number | 1 | Intensity of the glow effect |
children | React.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>
);
}