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

React Router Adapter

React Router v6/v7 adapter: enable cross-route tours with useLocation and useNavigate integration in SPAs

domidex01Published

Router adapter for React Router v6 (react-router-dom) and v7 (react-router).

When to Use

Use this adapter when:

  • You're building a React SPA with Vite, Create React App, or similar
  • Your app uses React Router for client-side routing
  • You need multi-page tours that navigate between routes

Installation

The adapter is included in @tour-kit/react. Install React Router:

pnpm add @tour-kit/react react-router
pnpm add @tour-kit/react react-router-dom

Usage

import { BrowserRouter } from 'react-router-dom'; // or 'react-router'
import { MultiTourKitProvider, useReactRouter } from '@tour-kit/react';

function TourProviderWithRouter({ children }) {
  const router = useReactRouter();

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

function App() {
  return (
    <BrowserRouter>
      <TourProviderWithRouter>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </TourProviderWithRouter>
    </BrowserRouter>
  );
}
import { useLocation, useNavigate } from 'react-router-dom';
import { MultiTourKitProvider, createReactRouterAdapter } from '@tour-kit/react';

const useRouter = createReactRouterAdapter(useLocation, useNavigate);

function TourProviderWithRouter({ children }) {
  const router = useRouter();

  return (
    <MultiTourKitProvider router={router}>
      {children}
    </MultiTourKitProvider>
  );
}

The adapter must be used inside BrowserRouter (or any Router) since it depends on React Router's context.


useReactRouter

Direct hook that automatically imports from react-router (v7) or react-router-dom (v6).

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

const router = useReactRouter();

Return Value

Returns a RouterAdapter with these methods:

Prop

Type


createReactRouterAdapter

Factory function for custom setups.

import { createReactRouterAdapter } from '@tour-kit/react';
import { useLocation, useNavigate } from 'react-router-dom';

const useRouter = createReactRouterAdapter(useLocation, useNavigate);

Parameters

Prop

Type


Complete Example

src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import {
  MultiTourKitProvider,
  useReactRouter,
  Tour,
  TourStep,
  TourOverlay,
  TourCard,
  useTour,
} from '@tour-kit/react';
import { Home, Dashboard, Settings } from './pages';

function TourProviderWithRouter({ children }) {
  const router = useReactRouter();

  return (
    <MultiTourKitProvider
      router={router}
      routePersistence={{
        enabled: true,
        storage: 'sessionStorage', // Clears on tab close
      }}
      autoNavigate={true}
    >
      <Tour id="onboarding">
        <TourStep
          target="#hero"
          title="Welcome!"
          content="Let's show you around our app."
          route="/"
          routeMatch="exact"
        />
        <TourStep
          target="#dashboard-chart"
          title="Your Dashboard"
          content="View your analytics here."
          route="/dashboard"
        />
        <TourStep
          target="#settings-theme"
          title="Settings"
          content="Customize your experience."
          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 btn-primary"
    >
      Start Tour
    </button>
  );
}

export default function App() {
  return (
    <BrowserRouter>
      <TourProviderWithRouter>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </TourProviderWithRouter>
    </BrowserRouter>
  );
}

React Router v6 vs v7

The adapter works with both versions:

Featurev6 (react-router-dom)v7 (react-router)
Packagereact-router-domreact-router
ImportAuto-detectedAuto-detected
APISameSame
DetectionFalls back if v7 not foundTried first
// Adapter tries in order:
try {
  require('react-router');     // v7
} catch {
  require('react-router-dom'); // v6 fallback
}

Route Change Detection

The adapter uses location.pathname reactivity:

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

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


Nested Routes

For nested routes, use startsWith matching:

// Routes setup
<Route path="/dashboard/*" element={<Dashboard />}>
  <Route path="analytics" element={<Analytics />} />
  <Route path="settings" element={<Settings />} />
</Route>

// Tour step that works for all dashboard routes
<TourStep
  target="#dashboard-nav"
  title="Dashboard"
  route="/dashboard"
  routeMatch="startsWith"  // Matches /dashboard, /dashboard/analytics, etc.
/>

Hash Router Support

For HashRouter, the adapter still works since useLocation() returns the same shape:

import { HashRouter } from 'react-router-dom';

function App() {
  return (
    <HashRouter>
      <TourProviderWithRouter>
        <Routes />
      </TourProviderWithRouter>
    </HashRouter>
  );
}

Troubleshooting

"Neither react-router nor react-router-dom found"

Install one of them:

pnpm add react-router-dom  # v6
# or
pnpm add react-router      # v7

"useLocation must be used within a Router"

Ensure the adapter is used inside a Router:

// Wrong - adapter outside router
<TourProviderWithRouter>
  <BrowserRouter>
    <Routes />
  </BrowserRouter>
</TourProviderWithRouter>

// Correct - adapter inside router
<BrowserRouter>
  <TourProviderWithRouter>
    <Routes />
  </TourProviderWithRouter>
</BrowserRouter>

Routes Not Matching

Check the current pathname:

const { getCurrentRoute } = useReactRouter();
console.log(getCurrentRoute()); // Check actual pathname