Original by Plinth · MIT
Buttons
Magnetic Button
A button that leans toward your cursor and springs back when you leave.
Install
npx shadcn@latest add https://plinthui.com/r/magnetic-button.json'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>
);
}