<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hyperiux Immersion Labs</title>
    <description>The latest articles on DEV Community by Hyperiux Immersion Labs (hyperiux).</description>
    <link>https://dev.to/hyperiux</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F13293%2F9ebbd377-56df-493e-974b-6169a8690ad3.png</url>
      <title>DEV Community: Hyperiux Immersion Labs</title>
      <link>https://dev.to/hyperiux</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hyperiux"/>
    <language>en</language>
    <item>
      <title>Installing Hyperiux Vault in Next.js: From CLI to a Production-Ready Cursor Effect</title>
      <dc:creator>Hyperiux</dc:creator>
      <pubDate>Fri, 11 Sep 2026 13:46:51 +0000</pubDate>
      <link>https://dev.to/hyperiux/installing-hyperiux-vault-in-nextjs-from-cli-to-a-production-ready-cursor-effect-mk1</link>
      <guid>https://dev.to/hyperiux/installing-hyperiux-vault-in-nextjs-from-cli-to-a-production-ready-cursor-effect-mk1</guid>
      <description>&lt;p&gt;Most React projects already have the structural UI covered: navigation, cards, grids, forms, responsive layouts.&lt;/p&gt;

&lt;p&gt;The harder part is often the interaction layer — cursor effects, text reveals, scroll sequences, transitions, and other motion that sits between those components.&lt;/p&gt;

&lt;p&gt;We can build those effects ourselves with GSAP, Motion, CSS, or WebGL. But when an interaction pattern already exists, starting from reusable source can save implementation time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vault.hyperiux.com/" rel="noopener noreferrer"&gt;Hyperiux Vault&lt;/a&gt; provides creative effects for React and Next.js, including cursor interactions, text animations, scroll effects, navigation effects, loaders, page transitions, backgrounds, and WebGL scenes.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll install &lt;strong&gt;Phantom Image Trail&lt;/strong&gt;, a GSAP-powered cursor effect, into a Next.js project.&lt;/p&gt;

&lt;p&gt;Then we'll:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;initialize Vault&lt;/li&gt;
&lt;li&gt;install the effect&lt;/li&gt;
&lt;li&gt;inspect the files added to the project&lt;/li&gt;
&lt;li&gt;render it on a real page&lt;/li&gt;
&lt;li&gt;customize the animation&lt;/li&gt;
&lt;li&gt;adjust its accessibility and mobile behavior&lt;/li&gt;
&lt;li&gt;verify everything with a production build&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Version note:&lt;/strong&gt; Vault is actively evolving, so record your CLI version before following the tutorial:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If generated paths or implementation details differ from the examples below, use the files produced by your installed CLI as the source of truth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How Hyperiux Vault works
&lt;/h2&gt;

&lt;p&gt;Vault uses a &lt;strong&gt;source-first&lt;/strong&gt; approach.&lt;/p&gt;

&lt;p&gt;Instead of importing the visual component from a Vault runtime package, the CLI copies the selected effect's source files into your project.&lt;/p&gt;

&lt;p&gt;You then import that local code from your application.&lt;/p&gt;

&lt;p&gt;That means we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect the animation implementation&lt;/li&gt;
&lt;li&gt;change GSAP timing and easing&lt;/li&gt;
&lt;li&gt;change styling&lt;/li&gt;
&lt;li&gt;modify responsive behavior&lt;/li&gt;
&lt;li&gt;adapt accessibility behavior&lt;/li&gt;
&lt;li&gt;maintain the source alongside the rest of the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a tradeoff.&lt;/p&gt;

&lt;p&gt;Once the implementation lives in our repository, accessibility, upgrades, compatibility, testing, and performance changes become our responsibility too.&lt;/p&gt;

&lt;p&gt;We'll see a practical example of that later in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The current Vault documentation supports setups including:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Supported setup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;18.17+; Node 20+ recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;React 18+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;Next.js 14 or 15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;td&gt;v3 or v4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;TypeScript recommended; modern JavaScript supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package managers&lt;/td&gt;
&lt;td&gt;npm, pnpm, yarn, or bun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project structure&lt;/td&gt;
&lt;td&gt;App Router, Pages Router, or modern Vite React&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For this tutorial we'll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js App Router&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified existing project might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── src/
│   └── app/
│       ├── globals.css
│       ├── layout.tsx
│       └── page.tsx
├── public/
├── package.json
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;src/&lt;/code&gt; is not required by Vault.&lt;/p&gt;

&lt;p&gt;If your project doesn't use it, keep your existing structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before changing an existing project
&lt;/h2&gt;

&lt;p&gt;Vault writes source into our repository, so it is worth creating a clean checkpoint first.&lt;/p&gt;

&lt;p&gt;If you're using Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit your current work, or otherwise make sure you can restore the project.&lt;/p&gt;

&lt;p&gt;Also record the Vault CLI version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation we'll inspect the project diff rather than assuming which files changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Hyperiux Vault
&lt;/h2&gt;

&lt;p&gt;From the project root, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vault initializes configuration for the project.&lt;/p&gt;

&lt;p&gt;The current CLI structure uses a root-level configuration file named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hyperiux.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration tells Vault how the project is structured and where different source files belong.&lt;/p&gt;

&lt;p&gt;For example, an aliases section may look similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@/components"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"effects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@/components/effects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@/hooks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@/lib"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;hyperiux.json&lt;/code&gt; generated in &lt;strong&gt;your&lt;/strong&gt; project rather than copying an example blindly.&lt;/p&gt;

&lt;p&gt;The most important value for this tutorial is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aliases.effects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That determines where Vault effects are written and helps explain the import path we'll use later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview the Phantom Image Trail installation
&lt;/h2&gt;

&lt;p&gt;Before installing the effect, you can inspect what the CLI plans to add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux add phantom-image-trail &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly useful when you're integrating Vault into an existing codebase.&lt;/p&gt;

&lt;p&gt;Now install the effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux add phantom-image-trail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;phantom-image-trail&lt;/code&gt; is the Vault effect we'll use throughout this tutorial.&lt;/p&gt;

&lt;p&gt;It uses &lt;strong&gt;GSAP&lt;/strong&gt; for the animation.&lt;/p&gt;

&lt;p&gt;After installation, verify that GSAP exists in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;ls &lt;/span&gt;gsap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it is missing, install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;gsap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Browse available Vault effects
&lt;/h2&gt;

&lt;p&gt;If you want to see the effects currently available through the CLI, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command shows &lt;strong&gt;all available Vault effects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not a list of effects already installed in your application.&lt;/p&gt;

&lt;p&gt;For installation verification, we'll instead use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the generated source files&lt;/li&gt;
&lt;li&gt;the project diff&lt;/li&gt;
&lt;li&gt;the resolved React import&lt;/li&gt;
&lt;li&gt;the running application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those checks tell us much more about whether the integration actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect exactly what changed
&lt;/h2&gt;

&lt;p&gt;After installing the effect, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--short&lt;/span&gt;
git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for any source-first tool.&lt;/p&gt;

&lt;p&gt;Rather than assuming the CLI touched only one directory, we can inspect the actual changes made by the version we're using.&lt;/p&gt;

&lt;p&gt;Pay attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hyperiux.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;effect source files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;lockfiles&lt;/li&gt;
&lt;li&gt;stylesheets&lt;/li&gt;
&lt;li&gt;any other modified project files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something unexpected changed, investigate it before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What source did Vault add?
&lt;/h2&gt;

&lt;p&gt;The exact generated filenames and directories can vary with project configuration and Vault versions.&lt;/p&gt;

&lt;p&gt;For a TypeScript project using &lt;code&gt;src/&lt;/code&gt;, the result may resemble:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── hyperiux.json
├── src/
│   ├── app/
│   │   └── page.tsx
│   └── components/
│       └── effects/
│           └── phantom-image-trail/
│               ├── index.tsx
│               ├── useMouse.ts
│               └── createSuspendedRaf.ts
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the files your CLI actually generated.&lt;/p&gt;

&lt;p&gt;The three main pieces have different responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; contains the component and the GSAP trail animation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useMouse&lt;/code&gt; tracks pointer movement&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createSuspendedRaf&lt;/code&gt; manages animation-frame work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the useful parts of Vault's model: if we later want to change pointer behavior, spawning rules, or the animation lifecycle, we know where to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Phantom Image Trail to the page
&lt;/h2&gt;

&lt;p&gt;Assuming your Vault effects alias resolves to &lt;code&gt;@/components/effects&lt;/code&gt;, import the component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PhantomImageTrail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/effects/phantom-image-trail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your &lt;code&gt;hyperiux.json&lt;/code&gt; uses another path, use that instead.&lt;/p&gt;

&lt;p&gt;Now update &lt;code&gt;src/app/page.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PhantomImageTrail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/effects/phantom-image-trail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pub-8abee449136941f5b0a1cd2c014534e9.r2.dev/vault-listing-images/assets-images/h-11.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pub-8abee449136941f5b0a1cd2c014534e9.r2.dev/vault-listing-images/assets-images/h-12.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pub-8abee449136941f5b0a1cd2c014534e9.r2.dev/vault-listing-images/assets-images/h-13.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"min-h-screen overflow-hidden bg-[#f8fdfe]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PhantomImageTrail&lt;/span&gt;
        &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;enableRotation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;idleSpawn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;idleDelay&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;cursorOffsetX&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;cursorOffsetY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;popOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;fadeOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;idlePopOutMultiplier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;idleFadeMultiplier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;imageMultiplier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;idleSpawn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we expect images to appear when the pointer moves rather than spawning while the cursor is idle.&lt;/p&gt;

&lt;p&gt;The full-height wrapper is only demo framing. Phantom Image Trail does not require every production page to occupy exactly one viewport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix decorative image semantics
&lt;/h2&gt;

&lt;p&gt;For this example, the trail images are purely decorative.&lt;/p&gt;

&lt;p&gt;Normally that would mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is where inspecting the copied Vault source becomes useful.&lt;/p&gt;

&lt;p&gt;In the current Phantom Image Trail implementation, the image normalization logic uses a fallback similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;`Trail &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty string is falsy, so our intentional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gets replaced with something such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trail 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For decorative images, that's not what we want.&lt;/p&gt;

&lt;p&gt;Open the generated Phantom Image Trail entry file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;`Trail &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s2"&gt;`Trail &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nullish coalescing operator only falls back for &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An intentionally empty string remains empty.&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now correctly produces decorative image semantics.&lt;/p&gt;

&lt;p&gt;If your images contain meaningful project or product information, use appropriate alt text instead — and make sure the same information exists independently of the cursor animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the application
&lt;/h2&gt;

&lt;p&gt;Start Next.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the URL printed by Next.js, normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the pointer across the page.&lt;/p&gt;

&lt;p&gt;You should see images appear along the pointer path, animate into view, and fade away.&lt;/p&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the component renders&lt;/li&gt;
&lt;li&gt;pointer movement produces the trail&lt;/li&gt;
&lt;li&gt;GSAP resolves correctly&lt;/li&gt;
&lt;li&gt;Tailwind styling is present&lt;/li&gt;
&lt;li&gt;there are no console errors&lt;/li&gt;
&lt;li&gt;there are no hydration warnings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A successful CLI command isn't enough. The useful verification is seeing the interaction work inside the real application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the images
&lt;/h2&gt;

&lt;p&gt;Now let's replace the demo images.&lt;/p&gt;

&lt;p&gt;Add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public/
└── images/
    ├── project-01.jpg
    ├── project-02.jpg
    └── project-03.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/images/project-01.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/images/project-02.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/images/project-03.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we already adjusted the source normalization, those empty strings remain decorative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce the trail intensity
&lt;/h2&gt;

&lt;p&gt;Let's make the effect calmer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PhantomImageTrail&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;enableRotation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;popOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;fadeOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;imageMultiplier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PhantomImageTrail&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;enableRotation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;triggerDistance&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;popOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;fadeOutDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;imageMultiplier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;triggerDistance&lt;/code&gt; controls how far the pointer travels before another image is introduced.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;imageMultiplier&lt;/code&gt; deserves extra attention.&lt;/p&gt;

&lt;p&gt;The implementation effectively uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source image count × imageMultiplier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So three source images with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;imageMultiplier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can result in nine trail image elements being available to the animation.&lt;/p&gt;

&lt;p&gt;That means &lt;code&gt;imageMultiplier&lt;/code&gt; affects performance as well as aesthetics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make mobile behavior intentional
&lt;/h2&gt;

&lt;p&gt;Cursor effects are primarily designed around fine-pointer devices.&lt;/p&gt;

&lt;p&gt;For production, we generally don't want a decorative desktop cursor trail to become required touch interaction.&lt;/p&gt;

&lt;p&gt;The current implementation includes mobile-related behavior such as mobile detection and tap spawning.&lt;/p&gt;

&lt;p&gt;The source currently exposes defaults equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;disableOnMobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;enableMobileTap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means touch interaction may still spawn decorative trail images.&lt;/p&gt;

&lt;p&gt;For this tutorial, we want coarse-pointer users to keep normal touch behavior.&lt;/p&gt;

&lt;p&gt;Because these are implementation-level details rather than something we should assume is permanent public API, we'll change the owned source.&lt;/p&gt;

&lt;p&gt;Update the defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;disableOnMobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;enableMobileTap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then make the tap handler explicitly respect &lt;code&gt;disableOnMobile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;enableMobileTap&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isMobileRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;disableOnMobile&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;enableMobileTap&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isMobileRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also include &lt;code&gt;disableOnMobile&lt;/code&gt; in that callback's dependency array where appropriate.&lt;/p&gt;

&lt;p&gt;Now tapping on a touch-first device does not spawn decorative trail images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make reduced motion actually disable the trail
&lt;/h2&gt;

&lt;p&gt;The effect source also detects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but detection alone isn't enough.&lt;/p&gt;

&lt;p&gt;For our final implementation, users requesting reduced motion should not receive the pointer trail.&lt;/p&gt;

&lt;p&gt;Find the function responsible for spawning the next image.&lt;/p&gt;

&lt;p&gt;If it currently starts with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;totalImages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;change it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefersReducedMotion&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;totalImages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure &lt;code&gt;prefersReducedMotion&lt;/code&gt; appears in the callback's dependency list.&lt;/p&gt;

&lt;p&gt;We can also stop idle spawning immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;prefersReducedMotion&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;idleSpawn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;disableOnMobile&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isMobileRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if your frame loop continues processing pointer animation work, give it an early return too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefersReducedMotion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the generated implementation contains UI copy saying that the trail continues under reduced motion, remove or update that message because it no longer reflects our behavior.&lt;/p&gt;

&lt;p&gt;Our fallback is simple: the page remains usable without the decorative trail.&lt;/p&gt;

&lt;p&gt;That's exactly what we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;page.tsx&lt;/code&gt; doesn't need &lt;code&gt;"use client"&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Phantom Image Trail depends on browser-side behavior such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pointer movement&lt;/li&gt;
&lt;li&gt;&lt;code&gt;window.matchMedia&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;animation frames&lt;/li&gt;
&lt;li&gt;measurements&lt;/li&gt;
&lt;li&gt;GSAP timelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the effect itself belongs behind a Client Component boundary.&lt;/p&gt;

&lt;p&gt;The generated implementation already establishes that boundary with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;page.tsx&lt;/code&gt; does not need the same directive.&lt;/p&gt;

&lt;p&gt;A Next.js Server Component can render a Client Component.&lt;/p&gt;

&lt;p&gt;The boundary begins at the client-marked module.&lt;/p&gt;

&lt;p&gt;We also don't need to reach for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(...,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;automatically.&lt;/p&gt;

&lt;p&gt;Use that pattern when an implementation genuinely cannot participate in server rendering. Don't disable SSR by reflex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The import cannot be resolved
&lt;/h3&gt;

&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hyperiux.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and inspect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aliases.effects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compare it with the directory created by the CLI.&lt;/p&gt;

&lt;p&gt;Start with the filesystem, not guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  GSAP cannot be resolved
&lt;/h3&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;ls &lt;/span&gt;gsap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If GSAP is absent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;gsap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Styles are missing
&lt;/h3&gt;

&lt;p&gt;Vault supports Tailwind CSS v3 and v4.&lt;/p&gt;

&lt;p&gt;For Tailwind v3, make sure your &lt;code&gt;content&lt;/code&gt; configuration scans the directory where the effect was generated.&lt;/p&gt;

&lt;p&gt;Tailwind v4 uses a different source-detection model, so debug the actual v4 setup rather than blindly copying a v3 configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  My screen reader announces “Trail 1”
&lt;/h3&gt;

&lt;p&gt;Check the image normalization change.&lt;/p&gt;

&lt;p&gt;For decorative images, explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;must survive normalization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Images still spawn when I tap on mobile
&lt;/h3&gt;

&lt;p&gt;Inspect the generated mobile tap handler.&lt;/p&gt;

&lt;p&gt;Make sure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enableMobileTap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is disabled and that your handler also respects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;disableOnMobile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reduced motion still shows the trail
&lt;/h3&gt;

&lt;p&gt;Confirm that &lt;code&gt;prefersReducedMotion&lt;/code&gt; prevents the image-spawning function itself from running.&lt;/p&gt;

&lt;p&gt;Showing a reduced-motion notice while continuing the same animation is not the behavior we're targeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance dropped after replacing the images
&lt;/h3&gt;

&lt;p&gt;Animation cost is only part of the equation.&lt;/p&gt;

&lt;p&gt;Large photography also adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;network transfer&lt;/li&gt;
&lt;li&gt;image decoding&lt;/li&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;compositing work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try lowering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;imageMultiplier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and optimize the image files before assuming the GSAP timeline itself is the bottleneck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional CLI troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you want to inspect which commands your installed Vault CLI exposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Vault documentation also references:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for diagnosing project configuration issues.&lt;/p&gt;

&lt;p&gt;Because CLI capabilities can change by version, checking &lt;code&gt;--help&lt;/code&gt; first keeps the tutorial tied to the software actually installed in your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the production build
&lt;/h2&gt;

&lt;p&gt;Development mode isn't the final test for a Next.js animation.&lt;/p&gt;

&lt;p&gt;Build the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can expose problems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript failures&lt;/li&gt;
&lt;li&gt;unresolved imports&lt;/li&gt;
&lt;li&gt;build-time rendering errors&lt;/li&gt;
&lt;li&gt;dependency problems&lt;/li&gt;
&lt;li&gt;incorrect client/server boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your project uses the standard Next.js production script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test that build instead of judging performance only from &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping Phantom Image Trail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;npm run build&lt;/code&gt; succeeds&lt;/li&gt;
&lt;li&gt;[ ] the trail works with a fine pointer on desktop&lt;/li&gt;
&lt;li&gt;[ ] coarse-pointer devices keep normal touch behavior&lt;/li&gt;
&lt;li&gt;[ ] tapping on mobile does not spawn decorative trail images&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;prefers-reduced-motion: reduce&lt;/code&gt; prevents the trail from spawning&lt;/li&gt;
&lt;li&gt;[ ] decorative images preserve &lt;code&gt;alt=""&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] meaningful content exists independently of the cursor animation&lt;/li&gt;
&lt;li&gt;[ ] focus rings and clickable controls remain unobstructed&lt;/li&gt;
&lt;li&gt;[ ] there are no console or hydration warnings&lt;/li&gt;
&lt;li&gt;[ ] images are appropriately compressed&lt;/li&gt;
&lt;li&gt;[ ] performance is tested using the production build&lt;/li&gt;
&lt;li&gt;[ ] the post-install Git diff contains only changes you intend to keep&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;We didn't stop at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hyperiux add phantom-image-trail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We initialized Vault, inspected &lt;code&gt;hyperiux.json&lt;/code&gt;, previewed the installation, checked the resulting source files, rendered the GSAP image trail in Next.js, customized it, and changed the copied implementation so its decorative images, mobile behavior, and reduced-motion behavior match the production expectations we're testing against.&lt;/p&gt;

&lt;p&gt;That's the practical consequence of source-first components.&lt;/p&gt;

&lt;p&gt;The install command gives us a starting point. The code is ours after that.&lt;/p&gt;

&lt;p&gt;Browse &lt;a href="https://vault.hyperiux.com/" rel="noopener noreferrer"&gt;Hyperiux Vault&lt;/a&gt; and the &lt;a href="https://vault.hyperiux.com/docs" rel="noopener noreferrer"&gt;Vault documentation&lt;/a&gt;, find an interaction that solves a real design problem, and inspect what it actually does once it enters your repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interesting part starts after the install command.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>gsap</category>
    </item>
  </channel>
</rss>
