Custom cursor
Also called cursor follower or animated cursor
A designed shape, such as a dot or a ring, that replaces or trails the mouse pointer and reacts to what it passes over.
import { useState, type ReactNode } from 'react';
import {
motion,
useMotionValue,
useReducedMotion,
useSpring
} from 'motion/react';
const RING_SPRING = { stiffness: 300, damping: 28 };
type CursorState = 'hidden' | 'default' | 'hover';
export function CustomCursor({ children }: { children: ReactNode }) {
const x = useMotionValue(0);
const y = useMotionValue(0);
const ringX = useSpring(x, RING_SPRING);
const ringY = useSpring(y, RING_SPRING);
const reduceMotion = useReducedMotion();
const [state, setState] = useState<CursorState>('hidden');
return (
<div
onPointerMove={(event) => {
if (event.pointerType !== 'mouse') return;
x.set(event.clientX);
y.set(event.clientY);
const target = event.target as Element;
// Text fields keep the normal I-beam cursor.
if (target.closest('input, textarea')) setState('hidden');
else setState(target.closest('a, button') ? 'hover' : 'default');
}}
onPointerLeave={() => setState('hidden')}
className='pointer-fine:cursor-none pointer-fine:[&_*]:cursor-none pointer-fine:[&_input,&_textarea]:cursor-text'
>
{children}
<div
aria-hidden
className='pointer-events-none fixed inset-0 z-50 hidden pointer-fine:block'
>
{/* The dot tracks the pointer exactly; only the ring trails. */}
<motion.div
style={{ x, y }}
animate={{ opacity: state === 'default' ? 1 : 0 }}
className='absolute -top-1 -left-1 size-2 rounded-full bg-foreground'
/>
<motion.div
style={{ x: reduceMotion ? x : ringX, y: reduceMotion ? y : ringY }}
animate={{
opacity: state === 'hidden' ? 0 : 1,
scale: state === 'hover' ? 1.8 : 1
}}
className='absolute -top-4 -left-4 size-8 rounded-full border border-foreground/60'
/>
</div>
</div>
);
}Add a custom cursor for mouse users only (pointer: fine): hide the system cursor and draw an 8px dot that follows the mouse exactly plus a 32px ring that follows it with a spring (stiffness 300, damping 28). Over links and buttons, scale the ring to 1.8 and hide the dot. Keep the normal cursor in text inputs, and make the ring follow without lag when prefers-reduced-motion is set.
Paste into Layerwise or any AI coding tool.
What is Custom cursor?
A custom cursor hides the system arrow, or adds to it, and draws your own shape at the pointer position. A popular version pairs a small dot that tracks the mouse exactly with a larger ring that trails behind it with a little lag, then grows when it passes over a link or button.
It shows up on portfolios, agency sites, and creative landing pages, where personality matters more than speed. On product interfaces and long reading pages it usually gets in the way.
Keep the real pointer position exact: the dot should never lag, or clicking feels off. Enable it only for a mouse with @media (pointer: fine), keep the normal text cursor in inputs, and drop the trailing motion for people who prefer reduced motion. If you only want a different icon, CSS can do it alone with cursor: url(cursor.svg) 4 4, auto.
Often confused with
- Magnetic button
- A magnetic button moves the element toward the pointer. A custom cursor changes the pointer itself.
Related terms
All termsMagnetic button
A button that leans toward the mouse pointer as it gets close, then springs back when the pointer leaves.
Hover effect
A visual change, such as a lift, glow, or zoom, that happens when the mouse pointer moves over an element to show it can be clicked.
Spring animation
An animation driven by simulated spring physics in place of a fixed duration and curve, so motion can overshoot, settle, and react naturally.
Reduced motion
An operating system setting that asks apps to minimize movement, which websites detect with the prefers-reduced-motion media query.
Build it with Layerwise
Describe your app, watch it run in a live preview, and publish it with a database, storage, and sign-in built in.