Sticky Cursor
A sticky cursor replaces or augments the native pointer with a custom element that follows the mouse with smoothing, and reacts to what it hovers, growing over links, snapping to buttons, showing a label. It signals a crafted, considered site. Built with GSAP quickTo so the follow stays smooth without per-frame allocation.
Updated June 2, 2026
Components that solve this
Tuning the pattern
- Follow smoothing
- Custom Cursor defaults `cursor-speed` to 0.7 with `expo.out`, which gives a short, premium catch-up; a true 1:1 follow tracks every micro-tremor and reads cheap, while pushing past roughly 0.8s of lag reads as broken, so keep the smoothing in the 0.4 to 0.7s band where it hides jitter without falling behind.
- Hover reactions
- React to a small, consistent set, grow over links and swap a label over media (Custom Cursor exposes `setLabel` and `clearLabel` for exactly this); reacting to every element turns the cursor into noise, so the restraint line is to pick two or three meaningful states and let everything else pass through unchanged.
- Native cursor
- Hiding the native cursor (Custom Cursor `hide-cursor` true) is the fully crafted look but only safe on expressive surfaces, never on forms or dense UI; always keep a real fallback, and Custom Cursor already disables itself on touch and tablet (`disable` mobile, tablet) since there is no pointer to follow there.
When a sticky cursor fits
A custom cursor sets a tone of craft and intentionality. It works on sites where the experience is part of the product.
- Portfolio and agency sites where the cursor is part of the brand
- Product launches and campaign pages that want a signature feel
- Interactive showcases where the cursor reveals or labels content
- Editorial pieces with media that the cursor can preview or scrub
Never hide the cursor on functional UI
How the follow works
The cursor element is position: fixed and follows the pointer through two gsap.quickTo setters, one for x, one for y, with a short smoothing duration. Hover reactions are class or state changes that scale the cursor or swap its label, driven by mouseenter / mouseleave on interactive targets.
Annnimate components that solve this pattern
- The Custom Cursor component handles the smooth follow, hover-state reactions, label support, and a reduced-motion / touch fallback. It exposes an imperative API (
setLabel,clearLabel) for programmatic control - The Image Trail is a cursor-driven cousin: instead of one follower, it spawns a trail of images that chase the pointer
Common questions
- Why does my custom cursor stutter?
- Creating a new tween on every mousemove. Use two
gsap.quickTosetters (x and y) created once - they reuse a single tween instance per axis and just update the target value each event. That's the difference between smooth and stuttery at 60+ mousemoves per second. - How do I keep a custom cursor accessible?
- Keep the native cursor visible (or provide a clear fallback), never hide focus indicators, and make sure every interaction works with keyboard alone. Honor prefers-reduced-motion by disabling the smoothing or skipping the custom cursor entirely. On touch devices the custom cursor should not render at all.
- Should the follow be 1:1 or lagged?
- A short lag (0.3-0.4s smoothing) reads as 'premium' and hides micro-jitter. A true 1:1 follow can feel cheap because it tracks every tiny tremor. But too much lag (0.8s+) feels broken, like the cursor can't keep up. The 0.3-0.5s range is the sweet spot.
