Skip to main content
userTourKit
@tour-kit/reactRouter Adapters

Next.js App Router Adapter

Next.js App Router adapter: configure route-aware tours with usePathname integration for server component layouts

domidex01Published

Router adapter for Next.js 13+ App Router using next/navigation.

When to Use

Use this adapter when:

  • You're using Next.js 13 or later
  • Your app uses the App Router (app/ directory)
  • You need multi-page tours that navigate between routes

Installation

The adapter is included in @tour-kit/react. Make sure Next.js is installed:

pnpm add @tour-kit/react next

Usage

'use client';

import { MultiTourKitProvider, useNextAppRouter } from '@tour-kit/react';

export function TourProvider({ children }: { children: React.ReactNode }) {
  const router = useNextAppRouter();

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{ enabled: true }}
    >
      {children}
    </MultiTourKitProvider>
  );
}
'use client';

import { MultiTourKitProvider, createNextAppRouterAdapter } from '@tour-kit/react';
import { usePathname, useRouter } from 'next/navigation';

const useRouter = createNextAppRouterAdapter(usePathname, useRouter);

export function TourProvider({ children }: { children: React.ReactNode }) {
  const router = useRouter();

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{ enabled: true }}
    >
      {children}
    </MultiTourKitProvider>
  );
}

Both the adapter hook and any component using it must have 'use client' at the top since they use browser APIs and React hooks.


useNextAppRouter

Direct hook that automatically imports from next/navigation.

import { useNextAppRouter } from '@tour-kit/react';

const router = useNextAppRouter();

Return Value

Returns a RouterAdapter with these methods:

Prop

Type


createNextAppRouterAdapter

Factory function for custom setups.

import { createNextAppRouterAdapter } from '@tour-kit/react';
import { usePathname, useRouter } from 'next/navigation';

const useMyRouter = createNextAppRouterAdapter(usePathname, useRouter);

Parameters

Prop

Type


Complete Example

app/providers/tour-provider.tsx
'use client';

import {
  MultiTourKitProvider,
  useNextAppRouter,
  Tour,
  TourStep,
  TourOverlay,
  TourCard,
  useTour,
} from '@tour-kit/react';

export function TourProvider({ children }: { children: React.ReactNode }) {
  const router = useNextAppRouter();

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{
        enabled: true,
        storage: 'localStorage',
        expiryMs: 24 * 60 * 60 * 1000, // 24 hours
      }}
      autoNavigate={true}
      onTourComplete={(tourId) => {
        console.log(`Tour ${tourId} completed!`);
      }}
    >
      <Tour id="onboarding">
        <TourStep
          target="#hero-section"
          title="Welcome!"
          content="Let's show you around our app."
          route="/"
          routeMatch="exact"
        />
        <TourStep
          target="#dashboard-widget"
          title="Your Dashboard"
          content="View your metrics and activity here."
          route="/dashboard"
          routeMatch="startsWith"
        />
        <TourStep
          target="#settings-link"
          title="Settings"
          content="Customize your experience in settings."
          route="/settings"
        />
      </Tour>

      <TourOverlay />
      <TourCard />

      {children}

      <TourButton />
    </MultiTourKitProvider>
  );
}

function TourButton() {
  const { start, isActive } = useTour();

  if (isActive) return null;

  return (
    <button
      onClick={() => start('onboarding')}
      className="fixed bottom-4 right-4 px-4 py-2 bg-blue-500 text-white rounded-lg"
    >
      Start Tour
    </button>
  );
}
app/layout.tsx
import { TourProvider } from './providers/tour-provider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <TourProvider>
          {children}
        </TourProvider>
      </body>
    </html>
  );
}

SSR Handling

The adapter handles SSR gracefully:

  • usePathname() may return null during SSR edge cases - adapter defaults to '/'
  • Route change callbacks only fire client-side
  • Initial route is detected after hydration

Route Change Detection

The adapter uses usePathname() reactivity to detect route changes:

// Internal behavior:
useEffect(() => {
  if (previousPath !== pathname) {
    for (const callback of callbacks) {
      callback(pathname);
    }
  }
}, [pathname]);

The onRouteChange callback fires immediately with the current route when subscribed, then on each subsequent navigation.


Troubleshooting

"Cannot find module 'next/navigation'"

The adapter requires Next.js 13+. Check your Next.js version:

npm list next

Hydration Mismatch

Ensure tour components have 'use client':

'use client'; // Required!

import { Tour } from '@tour-kit/react';

Routes Not Matching

Check your routeMatch mode:

// For exact paths
<TourStep route="/" routeMatch="exact" />

// For paths with dynamic segments
<TourStep route="/dashboard" routeMatch="startsWith" />