Skip to content
Plinth

Original by Plinth · MIT

Buttons

Magnetic Button

A button that leans toward your cursor and springs back when you leave.

#button#magnetic#hover#javascript

Install

npx shadcn@latest add https://plinthui.com/r/magnetic-button.json
Open in v0
'use client';

import { useRef, useState } from 'react';

const STRENGTH = 0.3;
const MAX = 12;

export function MagneticButton({ children = 'Come closer' }: { children?: React.ReactNode }) {
  const zoneRef = useRef<HTMLDivElement>(null);
  const [offset, setOffset] = useState({ x: 0, y: 0 });
  const [tracking, setTracking] = useState(false);

  const clamp = (n: number) => Math.max(-MAX, Math.min(MAX, n * STRENGTH));

  const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
    const zone = zoneRef.current;
    if (!zone) return;
    const rect = zone.getBoundingClientRect();
    setOffset({
      x: clamp(event.clientX - (rect.left + rect.width / 2)),
      y: clamp(event.clientY - (rect.top + rect.height / 2)),
    });
    setTracking(true);
  };

  const handleMouseLeave = () => {
    setTracking(false);
    setOffset({ x: 0, y: 0 });
  };

  // No easing while tracking; the spring plays on the way back.
  const style: React.CSSProperties = {
    transform: `translate(${offset.x}px, ${offset.y}px)`,
    transition: tracking ? 'none' : 'transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1)',
    willChange: 'transform',
  };

  return (
    <div
      ref={zoneRef}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      className="grid h-20 w-40 place-items-center"
    >
      <button
        type="button"
        style={style}
        className="rounded-[10px] bg-[#1a1a1a] px-6 py-3 text-[15px] font-semibold text-[#faf9f6] dark:bg-[#ece9e2] dark:text-[#0e0f11]"
      >
        {children}
      </button>
    </div>
  );
}

More in Buttons

Similar tags