Componentregistry:component
Image Trail
Moving the pointer leaves a trail of images behind it. Each card rises under the cursor, tilts with the direction of travel, hangs for a moment and drifts away. Recycles a fixed pool of cards, keeps quiet zones clear for headlines and buttons, loads images lazily, mouse and pen only, with reduced-motion support.
Installation
$ npx shadcn@latest add @creative-ui/image-trailOn an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/image-trail.json
Preview
Move your pointer
this label is a quiet zone
Spacing80px
Duration1500ms
Tilt16°
Max cards14
Card shape
Usage
Example.tsx
import { ImageTrail } from "@/components/image-trail"
const images = [
{ src: "/art/bust.webp", alt: "Marble bust" },
{ src: "/art/muse.webp", alt: "A muse reading" },
{ src: "/art/owl.webp", alt: "An owl on a keyboard" },
]
export function Hero() {
return (
<section className="relative h-[80svh] overflow-hidden">
<ImageTrail images={images} />
{/* Cards never spawn over the headline and buttons. */}
<div data-trail-quiet className="relative z-10 mx-auto w-fit pt-24">
<h1>Timeless craft.</h1>
</div>
</section>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| images* | { src: string; alt?: string }[] | — | Images to drop along the pointer path, used in order and looped. |
| spacing | number | 80 | Pointer travel in px between two cards. Smaller is denser. |
| duration | number | 1500 | Life of one card in ms: rise, hang, drift away. |
| maxCards | number | 14 | Most cards on screen at once. Cards are recycled, oldest first, so the DOM never grows. |
| tilt | number | 16 | Largest tilt in degrees, following the direction of travel. 0 keeps cards upright. |
| quietSelector | string | "[data-trail-quiet]" | Selector for areas inside the parent that never spawn cards, plus half a card of margin. |
| cardClassName | string | — | Classes for every card: size, aspect ratio, radius, shadow. Defaults to a 4:5 card, 110 to 168px wide. |
Also accepts every div prop, which is spread onto the root element.
Notes
- Place it inside a positioned element (
relative,absolute,fixed). It covers that element, listens for pointer moves on it and stays out of the way of clicks. - Give your own content a higher
z-indexto keep it above the trail, or a lower one to let cards pass over it. - Mouse and pen only, so touch scrolling never sprays cards. Images load once the page is idle, and only on devices with a mouse or pen.
- Cards move with transform and opacity through the Web Animations API, so the trail costs no layout and no React renders while the pointer moves.
- With reduced motion the cards fade in place without rising, tilting or scaling.
- The trail is decorative and hidden from assistive tech. Put anything people need to read in the page itself.
- In Next.js, pass resized URLs from
getImageProps(next/image) so the trail does not download full-size images.
Dependencies
shadcn/utils
