← All primitives
MagneticButton
InteractionA button gently attracted to the cursor while hovered, springing back on leave. GSAP smoothing; respects reduced-motion.
Requires the .prism-magnetic style from globals.css.
$
npx shadcn@latest add https://prism.icglabs.co/r/magnetic-button.jsonDependencies:gsap
View raw manifest →components/prism/MagneticButton.tsx
"use client";
import { useRef } from "react";
import gsap from "gsap";
/**
* MagneticButton — an element that's gently attracted to the cursor while hovered,
* then springs back on leave. GSAP for the smoothing (already a dep). Honors
* prefers-reduced-motion (no transform applied).
*/
export function MagneticButton({
children,
className = "",
strength = 0.4,
onClick,
type = "button",
}: {
children: React.ReactNode;
className?: string;
/** 0–1: how strongly it follows the cursor. */
strength?: number;
onClick?: () => void;
type?: "button" | "submit";
}) {
const ref = useRef<HTMLButtonElement>(null);
function onMove(e: React.MouseEvent<HTMLButtonElement>) {
const el = ref.current;
if (!el) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const r = el.getBoundingClientRect();
const x = (e.clientX - (r.left + r.width / 2)) * strength;
const y = (e.clientY - (r.top + r.height / 2)) * strength;
gsap.to(el, { x, y, duration: 0.6, ease: "power3.out" });
}
function onLeave() {
const el = ref.current;
if (!el) return;
gsap.to(el, { x: 0, y: 0, duration: 0.7, ease: "elastic.out(1, 0.4)" });
}
return (
<button
ref={ref}
type={type}
onClick={onClick}
onMouseMove={onMove}
onMouseLeave={onLeave}
className={`prism-magnetic ${className}`}
>
{children}
</button>
);
}