shadimage
AnimBits
componentbuttons-magneticsource: ok · refreshed

Magnetic Button

Button with cursor-following magnetic attraction effect

npx shadcn add "https://animbits.dev/r/buttons-magnetic.json"
free

Interaction

cursor-follow

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · medium
animationmotion

Files2 · source cached 10/8/2026, 6:17:41 AM

registry/new-york/animations/buttons/magnetic.tsx
"use client";
import * as React from "react";
import { motion, HTMLMotionProps } from "motion/react";
import { cn } from "@/lib/utils";
import { useMagnetic, type UseMagneticOptions } from "@/lib/use-magnetic";
interface MagneticButtonProps
  extends Omit<HTMLMotionProps<"button">, "ref">,
    UseMagneticOptions {
  children: React.ReactNode;
}
export function MagneticButton({
  children,
  className,
  strength,
  stiffness,
  damping,
  ...props
}: MagneticButtonProps) {
  const { magneticProps, ref } = useMagnetic({ strength, stiffness, damping });
  return (
    <motion.button
      ref={ref as any}
      {...magneticProps}
      className={cn(className)}
      {...props}
    >
      {children}
    </motion.button>
  );
}
lib/use-magnetic.ts
import * as React from "react";
import { useMotionValue, useSpring } from "motion/react";

export interface UseMagneticOptions {
  strength?: number;
  stiffness?: number;
  damping?: number;
}

export function useMagnetic(options: UseMagneticOptions = {}) {
  const { strength = 30, stiffness = 150, damping = 15 } = options;

  const ref = React.useRef<HTMLElement>(null);
  const x = useMotionValue(0);
  const y = useMotionValue(0);

  const springConfig = { damping, stiffness };
  const springX = useSpring(x, springConfig);
  const springY = useSpring(y, springConfig);

  const handleMouseMove = (e: React.MouseEvent<HTMLElement>) => {
    if (!ref.current) return;

    const rect = ref.current.getBoundingClientRect();
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;

    const distanceX = e.clientX - centerX;
    const distanceY = e.clientY - centerY;

    x.set(distanceX * (strength / 100));
    y.set(distanceY * (strength / 100));
  };

  const handleMouseLeave = () => {
    x.set(0);
    y.set(0);
  };

  const magneticProps = {
    style: { x: springX, y: springY },
    onMouseMove: handleMouseMove,
    onMouseLeave: handleMouseLeave,
  };

  return { magneticProps, ref };
}

More from AnimBits

Browse all components in this library →