Magnetic Button
Button that warps toward the cursor with elastic spring physics
animationbuttonmagneticinteractive
Install dependencies
$npm install framer-motionPreview
Source Code
"use client";
import { useRef, useState } from "react";
import { motion, useSpring } from "framer-motion";
interface MagneticButtonProps {
children: React.ReactNode;
strength?: number;
radius?: number;
className?: string;
}
export default function MagneticButton({
children,
strength = 40,
radius = 200,
className = "",
}: MagneticButtonProps) {
const ref = useRef<HTMLButtonElement>(null);
const [isHovered, setIsHovered] = useState(false);
const springConfig = { damping: 15, stiffness: 150, mass: 0.1 };
const x = useSpring(0, springConfig);
const y = useSpring(0, springConfig);
const handleMouseMove = (e: React.MouseEvent) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const distX = e.clientX - centerX;
const distY = e.clientY - centerY;
const distance = Math.sqrt(distX * distX + distY * distY);
if (distance < radius) {
const pull = (1 - distance / radius) * strength;
x.set((distX / distance) * pull);
y.set((distY / distance) * pull);
if (!isHovered) setIsHovered(true);
} else {
x.set(0);
y.set(0);
if (isHovered) setIsHovered(false);
}
};
const handleMouseLeave = () => {
x.set(0);
y.set(0);
setIsHovered(false);
};
return (
<div
className="inline-flex items-center justify-center p-16"
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
>
<motion.button
ref={ref}
style={{ x, y }}
whileTap={{ scale: 0.95 }}
className={`relative cursor-pointer rounded-full bg-gradient-to-r from-indigo-500 to-purple-600 px-8 py-4 text-lg font-semibold text-white transition-shadow hover:shadow-[0_8px_30px_rgba(99,102,241,0.35)] ${className}`}
>
<motion.span
style={{
x: x.get() * 0.3,
y: y.get() * 0.3,
}}
className="relative z-10 block"
>
{children}
</motion.span>
</motion.button>
</div>
);
}