Cursor

Custom cursor follower that trails the pointer.

Report a bug

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/cursor.json

Storybook

Explore all variants, controls, and accessibility checks in the interactive Storybook playground.

View in Storybook

2 stories available:

Code

"use client"; import * as React from "react"; import { cn } from "../../lib/utils"; /** Props for {@link Cursor}. */ export type CursorProps = React.ComponentPropsWithoutRef<"div"> & { /** Diameter of the follower dot in pixels. Defaults to `24`. */ size?: number; }; type Point = { x: number; y: number; }; function usePointerPosition(): Point | undefined { const [point, setPoint] = React.useState<Point>(); React.useEffect(() => { if (typeof window === "undefined") { return; } const onMove = (event: PointerEvent): void => { setPoint({ x: event.clientX, y: event.clientY }); }; window.addEventListener("pointermove", onMove, { passive: true }); return () => { window.removeEventListener("pointermove", onMove); }; }, []); return point; } /** * Circular follower that tracks the pointer as a custom cursor overlay. * * Pointer tracking is direct feedback and keeps following under reduced * motion; `motion-reduce` drops the smoothing transition. * * @example * ```tsx * <Cursor size={32} /> * ``` */ export const Cursor = ({ className, ref, size = 24, style, ...props }: CursorProps & { ref?: React.Ref<HTMLDivElement> }) => { const point = usePointerPosition(); return ( <div aria-hidden="true" className={cn( "pointer-events-none fixed left-0 top-0 z-50 -translate-x-1/2 -translate-y-1/2 rounded-full border border-foreground bg-foreground/20 backdrop-invert transition-transform duration-100 ease-out motion-reduce:transition-none", point ? "opacity-100" : "opacity-0", className, )} ref={ref} style={{ height: `${size}px`, transform: point ? `translate(${point.x}px, ${point.y}px) translate(-50%, -50%)` : undefined, width: `${size}px`, ...style, }} {...props} /> ); }; Cursor.displayName = "Cursor";

Dependencies

  • @vllnt/ui@^0.2.1