The ImageTrail displays framed media following the user's pointer with physics-based momentum. It supports rounded frame variants, composable captions, custom keyframe animations, distance thresholds, and configurable stacking.
Preview
Installation
$ npx shadcn@latest add https://spark-ui-olive.vercel.app/r/image-trail.json
Usage
import {
ImageTrail,
ImageTrailItem,
ImageTrailItemCaption,
} from "@/components/image-trail";"use client";
const IMAGES = [
{ src: "/images/trail-1.jpg", label: "Forest crossing" },
{ src: "/images/trail-2.jpg", label: "Golden hour" },
{ src: "/images/trail-3.jpg", label: "Open coast" },
{ src: "/images/trail-4.jpg", label: "Highlands" },
{ src: "/images/trail-5.jpg", label: "Into the light" },
];
export default function ImageTrailDemo() {
return (
<div className="relative flex h-[600px] w-full cursor-crosshair items-center justify-center overflow-hidden rounded-2xl bg-zinc-50/50 dark:bg-zinc-950/30">
{/* Background grid representation */}
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:14px_24px] pointer-events-none" />
<ImageTrail
className="absolute inset-0"
threshold={90}
intensity={0.2}
repeatChildren={12}
keyframes={{
scale: [0.3, 1, 1, 0.3],
rotate: [-6, 0, 4, 0],
opacity: [0, 1, 1, 0],
}}
keyframesOptions={{
duration: 1.2,
times: [0, 0.05, 0.85, 1],
ease: "easeOut",
}}
trailElementAnimationKeyframes={{
x: { duration: 0.35, type: "tween", ease: "easeOut" },
y: { duration: 0.35, type: "tween", ease: "easeOut" },
}}
>
{IMAGES.map((image, index) => (
<ImageTrailItem
key={index}
radius="xl"
variant={index % 2 === 0 ? "framed" : "glass"}
className="h-38 w-30 sm:h-48 sm:w-38"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={image.src}
alt={image.label}
className="h-full w-full object-cover pointer-events-none select-none"
/>
<ImageTrailItemCaption>
<span className="mr-1.5 text-white/55">0{index + 1}</span>
{image.label}
</ImageTrailItemCaption>
</ImageTrailItem>
))}
</ImageTrail>
</div>
);
}Properties
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | "div" | HTML element to render the container as. |
threshold | number | 100 | Distance in pixels the mouse must travel to spawn the next element. |
intensity | number | 0.3 | Momentum factor (0.0001 to 1.0) for tracking lag. Lower is smoother. |
keyframes | DOMKeyframesDefinition | undefined | Framer Motion keyframes defining the animation sequence. |
keyframesOptions | AnimationOptions | undefined | Framer Motion options for the keyframes animation. |
trailElementAnimationKeyframes | object | tween/easeOut | Framer Motion options for the tracking movement. |
repeatChildren | number | 3 | Number of times children are duplicated to prevent trail gaps. |
variant | "plain" | "framed" | "glass" | "framed" | Visual treatment applied to ImageTrailItem. |
radius | "none" | "sm" | "md" | "lg" | "xl" | "full" | "xl" | Corner radius applied to an ImageTrailItem and its direct image. |
baseZIndex | number | 0 | Starting z-index for the trailing items. |
zIndexDirection | "new-on-top" | "old-on-top" | "new-on-top" | Stacking behavior ('new-on-top' or 'old-on-top'). |




