Cursor Image Trail
unlumen-ui/cursor-image-trailFreeComponent
A trail of images that follows the cursor with spring-physics fade and rotation.
Live preview
live · rendered by the registry
Rendered by Unlumen Ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.unlumen.com/r/cursor-image-trail.jsonSource
1"use client";23import * as React from "react";4import { AnimatePresence, motion } from "motion/react";56import { cn } from "@/lib/utils";78export interface CursorImageTrailProps {9 items: React.ReactNode[];10 /** Size of each trail item in px. @default 120 */11 itemSize?: number;12 /** Max simultaneous items in the trail. @default 8 */13 trailLength?: number;14 /** Minimum cursor travel (px) before spawning a new item. @default 80 */15 spawnDistance?: number;16 /** Max random rotation applied to each item in degrees. @default 20 */17 rotationRange?: number;18 /** Render target — defaults to the whole window. */19 containerRef?: React.RefObject<HTMLElement>;20 className?: string;21 children?: React.ReactNode;22}2324interface TrailItem {25 id: number;26 x: number;27 y: number;28 rotation: number;29 itemIndex: number;30}3132let _id = 0;33const nextId = () => ++_id;3435export function CursorImageTrail({36 items,37 itemSize = 120,38 trailLength = 8,39 spawnDistance = 80,40 rotationRange = 20,41 containerRef,42 className,43 children,44}: CursorImageTrailProps) {45 const [trail, setTrail] = React.useState<TrailItem[]>([]);46 const lastPos = React.useRef<{ x: number; y: number } | null>(null);47 const itemCounter = React.useRef(0);48 const containerElRef = React.useRef<HTMLDivElement>(null);4950 React.useEffect(() => {51 const el = containerRef?.current ?? containerElRef.current ?? window;5253 const onLeave = () => setTrail([]);5455 const onMove = (e: Event) => {56 const mouseEvent = e as MouseEvent;57 const rect =58 containerRef?.current?.getBoundingClientRect() ??59 containerElRef.current?.getBoundingClientRect();6061 const x = rect ? mouseEvent.clientX - rect.left : mouseEvent.clientX;62 const y = rect ? mouseEvent.clientY - rect.top : mouseEvent.clientY;6364 if (lastPos.current) {65 const dx = x - lastPos.current.x;66 const dy = y - lastPos.current.y;67 const dist = Math.sqrt(dx * dx + dy * dy);68 if (dist < spawnDistance) return;69 }7071 lastPos.current = { x, y };7273 const rotation = (Math.random() * 2 - 1) * rotationRange;74 const itemIndex = itemCounter.current % items.length;75 itemCounter.current += 1;7677 setTrail((prev) => {78 const next = [...prev, { id: nextId(), x, y, rotation, itemIndex }];79 return next.slice(-trailLength);80 });81 };8283 el.addEventListener("mousemove", onMove);84 el.addEventListener("mouseleave", onLeave);85 return () => {86// … truncated
What it pulls in
npm packages
Files it writes
More from Unlumen Ui
All 233 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| AI Chatai-chat | Unlumen Ui | Components | Paid | 2 deps | |
| Vercel Animate Countanimate-count | Unlumen Ui | Components | Free | 1 dep | |
| Animate Digitsanimate-digits | Unlumen Ui | Components | Free | 1 dep | |
| Animated Inputanimate-text-input-typing | Unlumen Ui | Components | Paid | 1 dep | |
| Animated Listanimated-list | Unlumen Ui | Components | Free | 1 dep | |
| Apple Switchapple-switch | Unlumen Ui | Components | Free | 1 dep | |
| Aurora Barsaurora-bars | Unlumen Ui | Components | Free | 1 dep | |
| Aurora Bluraurora-blur | Unlumen Ui | Components | Free | 2 deps |
