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

Next.js Pages Router Adapter

Next.js Pages Router adapter: set up multi-page tours with useRouter integration for _app.tsx tour providers

domidex01Published

Router adapter for Next.js Pages Router using next/router.

When to Use

Use this adapter when:

  • You're using Next.js with the Pages Router (pages/ directory)
  • Your app doesn't use the App Router
  • 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

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

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

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{ enabled: true }}
    >
      {children}
    </MultiTourKitProvider>
  );
}
import { MultiTourKitProvider, createNextPagesRouterAdapter } from '@tour-kit/react';
import { useRouter } from 'next/router';

const useRouter = createNextPagesRouterAdapter(useRouter);

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

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

useNextPagesRouter

Direct hook that automatically imports from next/router.

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

const router = useNextPagesRouter();

Return Value

Returns a RouterAdapter with these methods:

Prop

Type


createNextPagesRouterAdapter

Factory function for custom setups.

import { createNextPagesRouterAdapter } from '@tour-kit/react';
import { useRouter } from 'next/router';

const useMyRouter = createNextPagesRouterAdapter(useRouter);

Parameters

Prop

Type

Where NextRouter includes:

interface NextRouter {
  pathname: string;
  push: (url: string) => Promise<boolean>;
  events: {
    on: (event: string, handler: (url: string) => void) => void;
    off: (event: string, handler: (url: string) => void) => void;
  };
}

Complete Example

components/TourProvider.tsx
import {
  MultiTourKitProvider,
  useNextPagesRouter,
  Tour,
  TourStep,
  TourOverlay,
  TourCard,
  useTour,
} from '@tour-kit/react';

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

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{
        enabled: true,
        storage: 'localStorage',
      }}
      autoNavigate={true}
    >
      <Tour id="onboarding">
        <TourStep
          target="#hero"
          title="Welcome!"
          content="Let's show you around."
          route="/"
          routeMatch="exact"
        />
        <TourStep
          target="#dashboard"
          title="Dashboard"
          content="Your personal dashboard."
          route="/dashboard"
        />
        <TourStep
          target="#profile"
          title="Profile"
          content="Manage your profile here."
          route="/profile"
        />
      </Tour>

      <TourOverlay />
      <TourCard />

      {children}

      <TourButton />
    </MultiTourKitProvider>
  );
}

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

  if (isActive) return null;

  return (
    <button onClick={() => start('onboarding')}>
      Start Tour
    </button>
  );
}
pages/_app.tsx
import type { AppProps } from 'next/app';
import { TourProvider } from '@/components/TourProvider';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <TourProvider>
      <Component {...pageProps} />
    </TourProvider>
  );
}

Route Change Detection

The Pages Router adapter uses router.events for route change detection:

// Internal behavior:
useEffect(() => {
  const handleRouteChange = (url: string) => {
    const pathname = url.split('?')[0]; // Strip query string
    for (const callback of callbacks) {
      callback(pathname);
    }
  };

  router.events.on('routeChangeComplete', handleRouteChange);
  return () => router.events.off('routeChangeComplete', handleRouteChange);
}, [router.events]);

The onRouteChange callback fires immediately with the current route when subscribed, then fires on routeChangeComplete events.


Differences from App Router

FeaturePages RouterApp Router
Route sourcerouter.pathnameusePathname()
Navigationrouter.push() (async)router.push() (void)
Change detectionrouter.eventsPathname reactivity
Query stringStripped automaticallyNot in pathname
Client directiveNot needed'use client' required

Dynamic Routes

For routes with dynamic segments like /users/[id]:

// The pathname includes the pattern, not the actual value
router.pathname // "/users/[id]"

// Use startsWith for dynamic routes
<TourStep
  route="/users"
  routeMatch="startsWith"
/>

Troubleshooting

"Cannot find module 'next/router'"

Ensure you're using the Pages Router. The App Router uses next/navigation instead.

Route Changes Not Detected

Check that your navigation uses Next.js routing:

// This works - triggers routeChangeComplete
router.push('/dashboard');

// This doesn't trigger events - use Link instead
window.location.href = '/dashboard';

Query Parameters in Routes

The adapter automatically strips query parameters:

// URL: /dashboard?tab=overview
router.getCurrentRoute() // Returns: "/dashboard"