Cursor

A custom cursor, and a label that follows it, inside an area or across the page.

Move your pointer over this area

"use client"

import {
  Cursor,
  CursorContainer,
  CursorFollow,
  CursorProvider,
  type CursorFollowAlign,
  type CursorFollowSide,
} from "@animate-ui/react/primitives/cursor"

export default function CursorDemo({
  global = false,
  enableCursor = true,
  enableCursorFollow = true,
  side = "bottom",
  sideOffset = 15,
  align = "end",
  alignOffset = 5,
}: {
  global?: boolean
  enableCursor?: boolean
  enableCursorFollow?: boolean
  side?: CursorFollowSide
  sideOffset?: number
  align?: CursorFollowAlign
  alignOffset?: number
}) {
  return (
    <div
      className="flex h-72 w-full max-w-md items-center justify-center rounded-xl bg-accent"
    >
      <p className="font-medium text-muted-foreground italic">
        Move your pointer over this area
      </p>
      <CursorProvider global={global}>
        <CursorContainer>
          {enableCursor && (
            <Cursor>
              <svg
                className="size-6 text-foreground"
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 40 40"
                aria-hidden
              >
                <path
                  fill="currentColor"
                  d="M1.8 4.4 7 36.2c.3 1.8 2.6 2.3 3.6.8l3.9-5.7c1.7-2.5 4.5-4.1 7.5-4.3l6.9-.5c1.8-.1 2.5-2.4 1.1-3.5L5 2.5c-1.4-1.1-3.5 0-3.3 1.9Z"
                />
              </svg>
            </Cursor>
          )}
          {enableCursorFollow && (
            <CursorFollow
              side={side}
              sideOffset={sideOffset}
              align={align}
              alignOffset={alignOffset}
            >
              <div className="rounded-md bg-foreground px-2 py-1 text-sm text-background">
                Designer
              </div>
            </CursorFollow>
          )}
        </CursorContainer>
      </CursorProvider>
    </div>
  )
}

Installation

npm install @animate-ui/react motion
pnpm add @animate-ui/react motion
yarn add @animate-ui/react motion
bun add @animate-ui/react motion

Usage

import {
  Cursor,
  CursorContainer,
  CursorFollow,
  CursorProvider,
} from '@animate-ui/react/primitives/cursor';

export function Canvas() {
  return (
    <div className="relative h-72">
      <CursorProvider>
        <CursorContainer>
          <Cursor>
            <ArrowIcon />
          </Cursor>
          <CursorFollow side="bottom" align="end">
            <span>Designer</span>
          </CursorFollow>
        </CursorContainer>
      </CursorProvider>
    </div>
  );
}

By default, the cursor lives in the parent of CursorContainer. With global, it replaces the pointer on the whole page.

Animation

  • Entering the area hides the native pointer and pops the cursor and the follower in: they scale up from 0 on a spring with a little overshoot (stiffness 550, damping 30) while fading in (300ms). Leaving scales them back to 0 without overshoot and fades them out, then restores the pointer.
  • Cursor sticks to the pointer, its top-left corner on it (where an arrow's tip goes). CursorFollow trails it on a spring without bounce (stiffness 500, damping 50), so it feels attached without being rigid. It springs from where it last was: the first time, it flies in from near the area's top-left corner.
  • The position is driven by motion values with a hardware-accelerated transform (x / y): moving the pointer never re-renders React.
  • Touch pointers are ignored: a custom cursor only makes sense for pointers that hover.
  • <AnimateUIProvider tempo> paces the pop and the follow spring.
  • Under reduced motion, the cursors only fade in and out, and CursorFollow sticks to the pointer too.

API Reference

CursorProvider

PropTypeDefault
global?
booleanfalse
children
React.ReactNode-

CursorContainer

Marks the area: the cursor shows inside its parent element.

PropTypeDefault
asChild?
booleanfalse
...props?
HTMLMotionProps<'div'>-

Cursor

Its top-left corner sits on the pointer.

PropTypeDefault
asChild?
booleanfalse
children
React.ReactNode-
...props?
HTMLMotionProps<'div'>-

CursorFollow

PropTypeDefault
asChild?
booleanfalse
side?
'top' | 'right' | 'bottom' | 'left''bottom'
sideOffset?
number0
align?
'start' | 'center' | 'end''end'
alignOffset?
number0
transition?
SpringOptions{ stiffness: 500, damping: 50, bounce: 0 }
children
React.ReactNode-
...props?
Omit<HTMLMotionProps<'div'>, 'transition'>-

useCursor

Returns the cursor state: cursorX and cursorY motion values, active, global, containerRef, cursorRef, and cursorPos (a snapshot of the position when read).

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/18/2026

On this page