Fancy motion That still respects prefers-reduced-motion
How we respect prefers-reduced-motion across two animation libraries, Motion for transitions, GSAP for interactions, without removing ani...
The Blog
Custom cursors are often vanity. Ours is a real subsystem, motion values, easing, cursor modes, so we isolate and lazy-load it. Polish with a performance budget.
The pattern
A custom cursor sounds simple, follow the mouse, change shape on hover targets. In practice, ours is a substantial client module: motion values, ease-towards calculations, keyboard shortcut integration, and multiple cursor modes depending on what the visitor is hovering.
That's a lot of JavaScript. None of it has a meaningful server-rendered state, and none of it should block the content visitors came to see.
We load the entire cursor layer with dynamic(..., { ssr: false }) in the providers. The component doesn't exist during server rendering. It hydrates after the page is interactive. On devices without a precise pointer — phones, tablets — the cursor code never activates at all thanks to a matchMedia('(hover: hover)') check.
The point
The cursor is enhancement. It makes the site feel more crafted for desktop visitors with a mouse. It should never delay the first meaningful paint, inflate the initial bundle, or affect Core Web Vitals.
dynamic() with ssr: false handles all of this. The cursor loads in the background after the page is interactive. If it takes an extra moment to appear, nobody notices, because the content they came for is already rendered.
Not every site needs a custom cursor. Most probably shouldn't have one. But if you're going to build one, isolate it completely so it earns its place without costing performance.