Vite Integration
Configure User Tour Kit in Vite + React projects with hot module replacement, path aliases, and production build optimization
Vite Integration
Complete guide for setting up User Tour Kit with Vite and React. Covers single-page apps and React Router integration.
Installation
pnpm add @tour-kit/reactFor multi-page tours with React Router:
pnpm add @tour-kit/react react-router-domBasic Setup
Create a Tour Component
import {
Tour,
TourStep,
TourCard,
TourCardHeader,
TourCardContent,
TourCardFooter,
TourOverlay,
TourProgress,
TourNavigation,
TourClose,
useTour,
} from '@tour-kit/react';
export function ProductTour() {
return (
<Tour id="product-tour">
<TourStep
target="#welcome"
title="Welcome!"
content="Let's take a quick tour of our app."
placement="bottom"
/>
<TourStep
target="#features"
title="Features"
content="Here's what you can do."
placement="right"
/>
<TourStep
target="#get-started"
title="Get Started"
content="Click here to begin!"
placement="left"
/>
<TourOverlay />
<TourCard className="w-80">
<TourCardHeader>
<TourClose />
</TourCardHeader>
<TourCardContent />
<TourCardFooter>
<TourProgress variant="dots" />
<TourNavigation />
</TourCardFooter>
</TourCard>
<StartButton />
</Tour>
);
}
function StartButton() {
const { start, isActive } = useTour();
if (isActive) return null;
return (
<button
onClick={() => start()}
className="fixed bottom-4 right-4 px-4 py-2 bg-blue-500 text-white rounded-lg shadow-lg"
>
Start Tour
</button>
);
}Add to Your App
import { ProductTour } from './components/ProductTour';
import { Home } from './pages/Home';
export default function App() {
return (
<>
<ProductTour />
<Home />
</>
);
}Add Tailwind (Optional)
User Tour Kit uses Tailwind CSS classes. Make sure Tailwind is configured:
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@tour-kit/**/*.{js,ts,jsx,tsx}', // Include Tour Kit
],
theme: {
extend: {},
},
plugins: [],
};With React Router
For multi-page tours that span different routes:
Setup React Router
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<App />
</BrowserRouter>
);Create Multi-Page Tour
import {
Tour,
TourStep,
TourCard,
TourOverlay,
MultiTourKitProvider,
useReactRouter,
useTour,
} from '@tour-kit/react';
export function OnboardingTour() {
const router = useReactRouter();
return (
<MultiTourKitProvider
router={router}
routePersistence={{ enabled: true }}
autoNavigate={true}
>
<Tour id="onboarding">
{/* Home page step */}
<TourStep
target="#hero"
title="Welcome"
content="This is the homepage"
route="/"
routeMatch="exact"
/>
{/* Dashboard page step */}
<TourStep
target="#dashboard"
title="Dashboard"
content="Your personal dashboard"
route="/dashboard"
routeMatch="startsWith"
/>
{/* Settings page step */}
<TourStep
target="#settings"
title="Settings"
content="Customize your experience"
route="/settings"
/>
<TourOverlay />
<TourCard />
</Tour>
<TourTrigger />
</MultiTourKitProvider>
);
}
function TourTrigger() {
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>
);
}Add to App with Routes
import { Routes, Route } from 'react-router-dom';
import { OnboardingTour } from './components/OnboardingTour';
import { Home } from './pages/Home';
import { Dashboard } from './pages/Dashboard';
import { Settings } from './pages/Settings';
export default function App() {
return (
<>
<OnboardingTour />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard/*" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</>
);
}Custom Router Adapter
For advanced routing needs:
import { useNavigate, useLocation } from 'react-router-dom';
import { createReactRouterAdapter } from '@tour-kit/react';
function MyTour() {
const navigate = useNavigate();
const location = useLocation();
// Create custom adapter with hooks
const createRouter = createReactRouterAdapter(
() => navigate,
() => location
);
const router = createRouter();
return (
<MultiTourKitProvider router={router}>
{/* ... */}
</MultiTourKitProvider>
);
}With Hints
Add feature discovery hints:
import { HintsProvider, Hint } from '@tour-kit/hints';
export function FeatureHints() {
return (
<HintsProvider>
<Hint
id="new-export"
target="#export-btn"
content="New! Export to multiple formats"
color="success"
persist
/>
<Hint
id="keyboard-shortcuts"
target="#help-icon"
content="Press ? for keyboard shortcuts"
size="sm"
/>
</HintsProvider>
);
}import { ProductTour } from './components/ProductTour';
import { FeatureHints } from './components/FeatureHints';
export default function App() {
return (
<>
<ProductTour />
<FeatureHints />
{/* ... */}
</>
);
}Auto-Start on First Visit
import { usePersistence } from '@tour-kit/react';
import { useEffect, useState } from 'react';
export function ProductTour() {
const persistence = usePersistence({ keyPrefix: 'myapp' });
const [autoStart, setAutoStart] = useState(false);
useEffect(() => {
const completed = persistence.getCompletedTours().includes('welcome');
const dontShow = persistence.getDontShowAgain('welcome');
setAutoStart(!completed && !dontShow);
}, [persistence]);
return (
<Tour
id="welcome"
autoStart={autoStart}
onComplete={() => persistence.markCompleted('welcome')}
>
{/* ... */}
</Tour>
);
}Development Tips
Hot Module Replacement
Tours work seamlessly with Vite's HMR. Changes to tour steps update instantly.
Lazy Loading
For better performance, lazy load the tour component:
import { lazy, Suspense } from 'react';
const ProductTour = lazy(() => import('./components/ProductTour'));
export default function App() {
return (
<>
<Suspense fallback={null}>
<ProductTour />
</Suspense>
{/* ... */}
</>
);
}Environment-Specific Tours
export function ProductTour() {
// Only show in development
if (import.meta.env.DEV) {
return <DeveloperTour />;
}
// Production tour
return <ProductionTour />;
}TypeScript Configuration
Ensure your tsconfig.json includes:
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"strict": true
}
}Complete Example
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import './index.css';
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<App />
</BrowserRouter>
);import { Routes, Route } from 'react-router-dom';
import {
Tour,
TourStep,
TourCard,
TourOverlay,
MultiTourKitProvider,
useReactRouter,
useTour,
usePersistence,
} from '@tour-kit/react';
import { HintsProvider, Hint } from '@tour-kit/hints';
import { useEffect, useState } from 'react';
export default function App() {
const router = useReactRouter();
const persistence = usePersistence();
const [showTour, setShowTour] = useState(false);
useEffect(() => {
const completed = persistence.getCompletedTours().includes('main');
setShowTour(!completed);
}, [persistence]);
return (
<MultiTourKitProvider router={router} routePersistence={{ enabled: true }}>
<HintsProvider>
<Tour
id="main"
autoStart={showTour}
onComplete={() => persistence.markCompleted('main')}
>
<TourStep target="#nav" title="Navigation" content="..." route="/" />
<TourStep target="#dash" title="Dashboard" content="..." route="/dashboard" />
<TourOverlay />
<TourCard />
</Tour>
<Hint id="new-feature" target="#feature" content="New!" persist />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
<TourButton />
</HintsProvider>
</MultiTourKitProvider>
);
}
function TourButton() {
const { start, isActive } = useTour();
if (isActive) return null;
return (
<button onClick={() => start('main')} className="fixed bottom-4 right-4 btn">
Start Tour
</button>
);
}Troubleshooting
Tour Not Appearing
Ensure target elements exist in the DOM when the tour starts. Use waitForTarget for dynamically rendered elements.
Styles Missing
Add User Tour Kit to your Tailwind content paths or import the CSS directly.
Router Not Working
Make sure BrowserRouter wraps your entire app and useReactRouter is called within the router context.
Related
- Next.js Integration - For Next.js projects
- Router Integration - Deep dive into multi-page tours
- Examples - Copy-paste code examples