Magnetic Button

Button that is pulled toward the cursor with spring physics.

Installation

  1. Install the dependencies

    $ npm i motion lucide-react
  2. Copy the source into components/ui/magnetic-button.tsx

    components/ui/magnetic-button.tsx
    "use client";
    
    import { motion, useMotionValue, useSpring, type HTMLMotionProps } from "motion/react";
    import { Sparkles } from "lucide-react";
    import { useRef } from "react";
    
    type MagneticButtonProps = HTMLMotionProps<"button"> & {
      /** How far the button follows the cursor (0 - 1) */
      strength?: number;
    };
    
    export function MagneticButton({ children, strength = 0.35, className = "", ...props }: MagneticButtonProps) {
      const ref = useRef<HTMLButtonElement>(null);
      const x = useMotionValue(0);
      const y = useMotionValue(0);
      const springX = useSpring(x, { stiffness: 220, damping: 16, mass: 0.4 });
      const springY = useSpring(y, { stiffness: 220, damping: 16, mass: 0.4 });
    
      function handleMove(e: React.MouseEvent<HTMLButtonElement>) {
        const rect = ref.current?.getBoundingClientRect();
        if (!rect) return;
        x.set((e.clientX - (rect.left + rect.width / 2)) * strength);
        y.set((e.clientY - (rect.top + rect.height / 2)) * strength);
      }
    
      function reset() {
        x.set(0);
        y.set(0);
      }
    
      return (
        <motion.button
          ref={ref}
          onMouseMove={handleMove}
          onMouseLeave={reset}
          style={{ x: springX, y: springY }}
          whileTap={{ scale: 0.96 }}
          className={`inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-white px-7 py-3.5 text-sm font-medium text-zinc-900 shadow-sm transition-shadow hover:shadow-md ${className}`}
          {...props}
        >
          {children}
        </motion.button>
      );
    }
    
    export default function Demo() {
      return (
        <div className="grid place-items-center p-10">
          <MagneticButton>
            <Sparkles className="size-4 text-blue-600" />
            Hover me
          </MagneticButton>
        </div>
      );
    }
    
  3. Use it

    The file exports the component and a Demo showing typical usage. Import whichever you need.