PParable
All components

Cursor Image Trail

Visual Effects

A hover region that leaves a trail of images chasing the cursor — images from a pool pop in at pointer positions (throttled by distance) and fade/scale out a moment later, driven by a single rAF loop over a recycled pool of nodes with zero per-frame React state. Under prefers-reduced-motion the trail is disabled entirely and only the children render.

Inspired by Skiper UI — image cursor trail (pattern) — independently built, not affiliated. Attribution policy

Move across me

Installation

npx shadcn@latest add https://parable-three.vercel.app/r/cursor-image-trail.json

Runs the shadcn CLI — the component source is copied intocomponents/parable/cursor-image-trail.tsx. You own it from there.

Usage

The exact code rendering the preview above.

<CursorImageTrail
    className="grid h-80 w-full place-items-center rounded-xl bg-neutral-950"
    images={[
      "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjQiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjI0IDI1NiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjOGI1Y2Y2Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZWM0ODk5Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjIyNCIgaGVpZ2h0PSIyNTYiIHJ4PSIyOCIgZmlsbD0idXJsKCNnKSIvPjwvc3ZnPg==",
      "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjQiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjI0IDI1NiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZWM0ODk5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZjVhNjIzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjIyNCIgaGVpZ2h0PSIyNTYiIHJ4PSIyOCIgZmlsbD0idXJsKCNnKSIvPjwvc3ZnPg==",
      "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjQiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjI0IDI1NiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMjJkM2VlIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjOGI1Y2Y2Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjIyNCIgaGVpZ2h0PSIyNTYiIHJ4PSIyOCIgZmlsbD0idXJsKCNnKSIvPjwvc3ZnPg==",
      "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjQiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjI0IDI1NiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZjVhNjIzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZWM0ODk5Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjIyNCIgaGVpZ2h0PSIyNTYiIHJ4PSIyOCIgZmlsbD0idXJsKCNnKSIvPjwvc3ZnPg==",
      "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjQiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjI0IDI1NiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjOGI1Y2Y2Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMjJkM2VlIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjIyNCIgaGVpZ2h0PSIyNTYiIHJ4PSIyOCIgZmlsbD0idXJsKCNnKSIvPjwvc3ZnPg==",
    ]}
    spawnDistance={40}
    maxVisible={8}
  >
    <span className="pointer-events-none select-none font-mono text-[11px] uppercase tracking-[0.2em] text-white/45">
      Move across me
    </span>
  </CursorImageTrail>

Props

CursorImageTrailProps extends React.HTMLAttributes<HTMLDivElement>

PropTypeDefaultDescription
images*string[]Image URLs cycled through as the trail spawns. Required, non-empty.
maxVisiblenumber10Maximum images alive at once; the oldest gracefully retires past it.
spawnDistancenumber48Pointer travel, in px, between spawns. Larger = sparser trail.