component

Cursor Trail Reveal

Desktop cursor trail that reveals staggered image strips with clip-path wipes as the pointer moves.

Made by Axyl

Dependencies

class-variance-authorityutils

Installation

Usage

CursorTrailReveal is a client component with no default images — you must pass images. The trail only runs on desktop (viewport wider than desktopBreakpoint, default 1000px). On smaller screens it renders an empty layer and does nothing. When prefers-reduced-motion: reduce is set, the trail is disabled entirely.

Place it inside a relatively positioned section that fills the area you want the trail to cover (usually full viewport). Layer your copy above the trail with a higher z-index (for example z-[1]). The trail container uses pointer-events-none, so buttons and links in the overlay stay clickable.

Match maskColor to the section background so the clip-path strips blend in during the reveal.

import CursorTrailReveal from "@/components/sora-ui/effects/cursor-trail-reveal";
import TextRevealBlock from "@/components/sora-ui/texts/text-reveal-block";

const trailImages = Array.from(
  { length: 19 },
  (_, index) => `/demo/trail-images/trail-${index + 1}.jpg`
);

export default function ContactPage() {
  return (
    <section className="relative h-svh overflow-hidden bg-[#1a1a1a]">
      <CursorTrailReveal images={trailImages} maskColor="#1a1a1a" />

      <div className="relative z-[1] flex h-svh items-center justify-center">
        <TextRevealBlock animateOnScroll={false} blockColor="#c8e600">
          <h1>Your copy on top</h1>
        </TextRevealBlock>
      </div>
    </section>
  );
}

Images spawn only while the pointer moves inside the trail container and travels at least mouseThreshold pixels since the last spawn. URLs are cycled in order; when the list ends, it starts again from the first image.

Use the preview panel for the full contact-page demo with sample trail images.

Props

PropTypeDefault
images?
readonly string[]
-
maskColor?
string
"var(--background, #1a1a1a)"
imageSize?
number
175
mouseThreshold?
number
150
desktopBreakpoint?
number
1000
layout?
"default" | "fill"
"default"
className?
string
-
Loading preview...