← all components
#05

Magnetic glow-trail button

A button that is magnetically pulled toward your cursor while a trail of soft glowing dots fades behind the pointer. Two effects in one playful, tactile control. Available in React or plain HTML, CSS and JavaScript.

ReactJSCSS
// LIVE DEMO
share this component →FacebookX / TwitterLinkedIn
// THE CODE
React
"use client";
import { useRef } from "react";

export default function MagneticTrailButton() {
  const btnRef = useRef(null), wrapRef = useRef(null);
  const last = useRef({ x: 0, y: 0, ok: false });

  function move(e) {
    const btn = btnRef.current, wrap = wrapRef.current;
    const r = btn.getBoundingClientRect();
    const cx = r.left + r.width/2, cy = r.top + r.height/2;
    btn.style.transform = `translate(${(e.clientX-cx)*.35}px, ${(e.clientY-cy)*.5}px)`;
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const wr = wrap.getBoundingClientRect();
    const x = e.clientX - wr.left, y = e.clientY - wr.top;
    if (last.current.ok && Math.hypot(x-last.current.x, y-last.current.y) < 10) return;
    last.current = { x, y, ok: true };
    const dot = document.createElement("span");
    dot.className = "trail-dot"; dot.style.left = x + "px"; dot.style.top = y + "px";
    wrap.appendChild(dot);
    dot.addEventListener("animationend", () => dot.remove());
  }
  function leave() { btnRef.current.style.transform = "translate(0,0)"; last.current.ok = false; }

  return (
    <div ref={wrapRef} onPointerMove={move} onPointerLeave={leave}
      className="relative w-full h-40 flex items-center justify-center">
      <button ref={btnRef}
        className="relative z-10 px-9 py-4 rounded-2xl font-bold text-black
          bg-gradient-to-br from-sky-400 to-indigo-400 transition-transform duration-200 ease-out
          shadow-[0_0_30px_-8px_rgba(56,189,248,0.7)]">
        Catch me →
      </button>
      <style>{`
        .trail-dot { position:absolute; width:10px; height:10px; margin:-5px 0 0 -5px; border-radius:999px;
          background:radial-gradient(circle, rgba(129,140,248,.9), transparent 70%);
          pointer-events:none; animation:trail-fade .7s ease-out forwards; }
        @keyframes trail-fade { from { opacity:.9; transform:scale(1); } to { opacity:0; transform:scale(.3); } }
      `}</style>
    </div>
  );
}
// HOW IT WORKS
  • 01On pointer move, the offset from the button's center is scaled down and applied as a translate, so the button leans toward the cursor.
  • 02As the cursor moves, small glowing dots are spawned at its position and animate out with a fade+shrink, then remove themselves.
  • 03On pointer leave the button springs back to center. Trail spawning is skipped under prefers-reduced-motion.
Want a custom component for your product?
Let's build it →