🚀

Update available

We just released a new resource or update, refresh the Vault to access the latest version.

Cancel

Refresh now

Harri

Profile Picture

Harri

Lemke

Magnetic Cursor

Documentation

Webflow

Code

Setup: External Scripts

HTML

Copy
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/Flip.min.js"></script>

Step 1: Add HTML

HTML

Copy
<!-- Cursor follower -->
<div class="cursor">
  <div class="cursor-bg"></div>
</div>

<!-- Example of a link -->
<a data-magnetic-cursor-target href="#" class="magnetic-link">
  <div class="magnetic-link__inner">
    <span class="magnetic-link__label">Here’s a link</span>
    <svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 14 32" fill="none" class="magnetic-link__icon">
        <path d="M3 24L11 16L3 8" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round"></path>
    </svg>
  </div>
  <div data-magnetic-cursor-bg class="magnetic-link__bg"></div>
</a>

Step 2: Add CSS

CSS

Copy
.magnetic-link {
  color: inherit;
  padding: .5em .875em;
  text-decoration: none;
  position: relative;
}

.magnetic-link__inner {
  z-index: 1;
  grid-column-gap: .35em;
  grid-row-gap: .35em;
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.magnetic-link__icon {
  justify-content: center;
  align-items: center;
  width: .4em;
  margin-bottom: -.125em;
  display: flex;
}

.magnetic-link__bg {
  z-index: 0;
  border-radius: .25em;
  position: absolute;
  inset: 0%;
}

.cursor {
  aspect-ratio: 1;
  border-radius: 100em;
  width: .75em;
  position: fixed;
  inset: 0% auto auto 0%;
}

.cursor-bg {
  border-radius: inherit;
  background-color: #fff;
  width: 100%;
  height: 100%;
}

.magnetic-link .cursor-bg {
  opacity: 0.1;
}

Step 2: Add Javascript

Step 3: Add Javascript

Javascript

Copy
function initMagneticCursor() {  
  gsap.registerPlugin(Flip);
  
  const cursor = document.querySelector(".cursor");
  if(!cursor) return;
  
  const cursorBg = cursor.querySelector(".cursor-bg");
  
  // First, make the cursor div follow our mouse
  gsap.set(".cursor", {xPercent:-50, yPercent: -50});

  let xTo = gsap.quickTo(".cursor", "x", {duration: 0.6, ease: "power3"});
  let yTo = gsap.quickTo(".cursor", "y", {duration: 0.6, ease: "power3"});

  window.addEventListener("mousemove", e => {
    xTo(e.clientX);
    yTo(e.clientY);
  });
  
  // Then, make the setup for our flipping button
  const hoverTargets = document.querySelectorAll("[data-magnetic-cursor-target]");
  if(!hoverTargets.length) return;
  
  hoverTargets.forEach((target) => {
    const bgHolder = target.querySelector("[data-magnetic-cursor-bg]");
    
    target.addEventListener("mouseenter", () =>{
      // Register the 'state'
      const state = Flip.getState(cursorBg);
      
      // Add bg element into the link
      bgHolder.appendChild(cursorBg); 
      
      // Create the Flip animation
      Flip.from(state,{
        ease:"back.out(1)",
        duration: 0.3
      });
    });

    target.addEventListener("mouseleave", () =>{
      // Register 'state' again, include opacity for subtle ease
      const state = Flip.getState(cursorBg,{
        props:"opacity"
      });
      
      // Add bg element back into the cursor
      cursor.appendChild(cursorBg);
      
      // Flip for a smooth animation
      Flip.from(state,{
        ease:"power4.out",
        duration: 0.5,
      });
    });
  });
}

// Initialize Magnetic Cursor
document.addEventListener('DOMContentLoaded', () => {
  initMagneticCursor();
});

Implementation

Cursor

Use a fixed positioned element .cursor that follows the pointer via gsap.quickTo, and ensure it contains a single .cursor-bg child that flips between the cursor and targets.

<!-- Cursor follower -->
<div class="cursor">
  <div class="cursor-bg"></div>
</div>
Copy

Targets

Use [data-magnetic-cursor-target] to mark any hoverable element that should 'capture' the cursor’s background, enabling the Flip animation from the global cursor into the target.

Background Slot

Place a single child with [data-magnetic-cursor-bg] inside each target; this element acts as the drop-zone where the cursor’s .cursor-bg is appended on hover and Flip-animated into place. This doesn't necessarily have to be a full background, definitely checkout the live preview link to see how you can apply this script.

Flip Behavior

On mouseenter of a target, the script 'captures' state with Flip.getState(cursorBg) and appends the background element into the target’s [data-magnetic-cursor-bg], then Flip animates it by doing all the necessary calculations itself. On mouseleave, it 'captures' the state again, adds .cursor-bg back into .cursor, and flips it out smoothly.

Hover Styling

Style the cursor background while it lives inside a target using a parent selector, so the visual treatment changes only when the Flip has moved it into [data-magnetic-cursor-bg]. Here's an example:

/* This will set the background to 10% opacity, ONLY when its inside the link */
.magnetic-link .cursor-bg { 
  opacity: 0.1;
}
Copy

Live preview

Osmo Robot AI

Copy context for AI

Beta

Webflow

HTML/CSS/JS

Save video

Copy share link

Resource details

  • Published

    September 26, 2025

  • Category

    Cursor Animations

  • Popularity

    1.2K visitors

  • Need help?

    Join Slack

Cursor
Interactive
Sticky
Magnetic
GSAP
Flip
Ilja van EckIlja van Eck

Creator Credits

We always strive to credit creators as accurately as possible. While similar concepts might appear online, we aim to provide proper and respectful attribution.

s