Cross-platform guided tour library for React and React Native. Build interactive onboarding experiences with customizable tooltips, smart positioning, cross-page navigation, and async step actions. Fully themeable with TypeScript support.
npm install @robertlinde/react-tour-kitCross-platform guided tour library for React and React Native. Build interactive onboarding experiences with customizable tooltips, smart positioning, cross-page navigation, and async step actions. Fully themeable with TypeScript support.
- Cross-Platform: Works with React (web) and React Native
- Cross-Page Tours: Navigate between pages/screens during a tour
- Themeable: Customize colors without building custom components
- Fully Customizable: Replace the default tooltip and overlay with your own components
- Smart Positioning: Automatic tooltip positioning with viewport boundary detection
- Keyboard Navigation: Arrow keys and Escape (web) or hardware back button (Android)
- Async Step Actions: Run async code before each step (e.g., navigate, switch tabs, open dialogs)
- TypeScript First: Full TypeScript support
``bash`
npm install @robertlinde/react-tour-kit
Try the demos locally:
`bashWeb demo (React + React Router)
cd demos/react
npm install
npm run dev
Platform Guide
| Platform | Import from | Target elements with |
| ---------------- | ----------------------------- | --------------------------------------------- |
| React (Web) |
react-tour-kit/react | CSS selectors (e.g., [data-tour="welcome"]) |
| React Native | react-tour-kit/react-native | useTourTarget hook or refs |---
React (Web)
$3
#### 1. Wrap your app with TourProvider
`tsx
import {TourProvider} from '@robertlinde/react-tour-kit/react';function App() {
return (
);
}
`#### 2. Add data attributes to target elements
`tsx
function Dashboard() {
return (
Dashboard
);
}
`#### 3. Start a tour
`tsx
import {useTour, type TourStep} from '@robertlinde/react-tour-kit/react';const tourSteps: TourStep[] = [
{
target: '[data-tour="welcome"]', // CSS selector
title: 'Welcome!',
content: 'This is your dashboard. Let me show you around.',
placement: 'bottom',
},
{
target: '[data-tour="sidebar"]',
title: 'Navigation',
content: 'Use the sidebar to navigate between different sections.',
placement: 'right',
},
{
target: '[data-tour="settings"]',
title: 'Settings',
content: 'Click here to customize your preferences.',
placement: 'left',
},
];
function WelcomeButton() {
const {startTour} = useTour();
return ;
}
`$3
| Key | Action |
| --------------- | ------------- |
|
→ Arrow Right | Next step |
| ← Arrow Left | Previous step |
| Escape | Close tour |---
React Native
$3
#### 1. Wrap your app with TourProvider
`tsx
import {TourProvider} from '@robertlinde/react-tour-kit/react-native';function App() {
return (
);
}
`#### 2. Register target elements with useTourTarget
`tsx
import {View} from 'react-native';
import {useTourTarget} from '@robertlinde/react-tour-kit/react-native';function MyComponent() {
const welcomeRef = useTourTarget('welcome-button');
const settingsRef = useTourTarget('settings-button');
return (
Welcome
Settings
);
}
`#### 3. Start a tour
`tsx
import {useTour, type TourStep} from '@robertlinde/react-tour-kit/react-native';const tourSteps: TourStep[] = [
{
target: 'welcome-button', // String ID from useTourTarget
title: 'Welcome!',
content: 'This is your dashboard. Let me show you around.',
placement: 'bottom',
},
{
target: 'settings-button',
title: 'Settings',
content: 'Tap here to customize your preferences.',
placement: 'left',
},
];
function StartTourButton() {
const {startTour} = useTour();
return (
startTour(tourSteps, 'onboarding')}>
Start Tour
);
}
`$3
You can also pass refs directly instead of string IDs:
`tsx
import {useRef} from 'react';
import {useTour, type TourStep} from '@robertlinde/react-tour-kit/react-native';function MyComponent() {
const buttonRef = useRef(null);
const steps: TourStep[] = [
{
target: buttonRef, // Pass ref directly
title: 'Welcome!',
content: 'Tap here to get started.',
},
];
return ... ;
}
`$3
| Action | Trigger |
| ------------- | ------------------------------------------------- |
| Next step | Tap "Next" button |
| Previous step | Tap "Back" button |
| Close tour | Tap close button, overlay, or Android back button |
---
Cross-Page/Cross-Screen Tours
Tours can span multiple pages (web) or screens (React Native). The key is to:
1. Wrap your router/navigator with
TourProvider
2. Use onBeforeStep to navigate before showing a step$3
`tsx
import {BrowserRouter, useNavigate} from 'react-router-dom';
import {TourProvider, useTour, type TourStep} from '@robertlinde/react-tour-kit/react';// TourProvider wraps the router
function App() {
return (
} />
} />
);
}
// Define steps with navigation
function Home() {
const navigate = useNavigate();
const {startTour} = useTour();
const steps: TourStep[] = [
{
target: '[data-tour="welcome"]',
title: 'Welcome',
content: 'Let me show you around.',
placement: 'bottom',
},
{
target: '[data-tour="nav-settings"]',
title: 'Settings Link',
content: "Now let's visit the settings page.",
placement: 'bottom',
// Navigate before the NEXT step shows
onBeforeStep: async () => {
navigate('/settings');
await new Promise((r) => setTimeout(r, 100)); // Wait for navigation
},
},
{
target: '[data-tour="theme-toggle"]',
title: 'Theme Settings',
content: 'This element is on the Settings page!',
placement: 'right',
},
{
target: '[data-tour="welcome"]',
title: 'Back Home',
content: 'And we can navigate back.',
placement: 'bottom',
onBeforeStep: async () => {
navigate('/');
await new Promise((r) => setTimeout(r, 100));
},
},
];
return ;
}
`$3
`tsx
import {NavigationContainer, useNavigation} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {TourProvider, useTour, useTourTarget, type TourStep} from '@robertlinde/react-tour-kit/react-native';const Stack = createNativeStackNavigator();
// TourProvider wraps the navigator
function App() {
return (
);
}
// Define steps with navigation
function HomeScreen() {
const navigation = useNavigation();
const {startTour} = useTour();
const headerRef = useTourTarget('header');
const steps: TourStep[] = [
{
target: 'header',
title: 'Welcome',
content: 'Let me show you around.',
placement: 'bottom',
},
{
target: 'settings-header',
title: 'Settings Screen',
content: 'This element is on the Settings screen!',
placement: 'bottom',
onBeforeStep: async () => {
navigation.navigate('Settings');
await new Promise((r) => setTimeout(r, 300)); // Wait for animation
},
},
{
target: 'header',
title: 'Back Home',
content: 'And we can navigate back.',
placement: 'bottom',
onBeforeStep: async () => {
navigation.navigate('Home');
await new Promise((r) => setTimeout(r, 300));
},
},
];
return (
{/ ... /}
);
}
function SettingsScreen() {
const headerRef = useTourTarget('settings-header');
return {/ ... /} ;
}
`$3
1. TourProvider placement: Must wrap your router/navigator so tour state persists across navigation
2. Target availability: Targets must be mounted when their step is active. Use
onBeforeStep to navigate first
3. Wait for navigation: Add a small delay after navigation to ensure the new page/screen is rendered
4. Persistent elements: Elements like navigation bars that exist on all pages don't need onBeforeStep---
API Reference
$3
The provider component that enables tour functionality throughout your app.
`tsx
import {TourProvider} from '@robertlinde/react-tour-kit/react'; TooltipComponent={CustomTooltip} // Optional: custom tooltip component
OverlayComponent={CustomOverlay} // Optional: custom overlay component
theme={theme} // Optional: theme configuration
onTourEnd={(tourId) => {
// Optional: callback when tour ends
console.log(
Tour ${tourId} completed);
}}
>
{children}
;
`#### Props
| Prop | Type | Description |
| ------------------ | --------------------------------------------- | ---------------------------------------------------------- |
|
children | ReactNode | Your application content |
| theme | TourTheme | Theme configuration for colors (see Theming section) |
| i18n | TourI18n | Internationalization options for labels (see i18n section) |
| TooltipComponent | ForwardRefExoticComponent | Custom tooltip component (must use forwardRef) |
| OverlayComponent | ComponentType | Custom overlay component |
| onTourEnd | (tourId: string \| null) => void | Callback fired when tour ends (completed or closed) |$3
Access the tour context from any component.
`tsx
import {useTour} from '@robertlinde/react-tour-kit/react';function MyComponent() {
const {
isActive, // boolean: is a tour currently running?
currentStep, // number: current step index (0-based)
steps, // TourStep[]: all steps in current tour
currentTourId, // string | null: ID of current tour
startTour, // (steps: TourStep[], tourId?: string) => void
endTour, // () => void
nextStep, // () => void
prevStep, // () => void
goToStep, // (step: number) => void
} = useTour();
}
`$3
Define the steps in your tour.
`tsx
type TourStep = {
target: TourTarget; // Target element (see below)
title: string; // Step title
content: string; // Step description/content
placement?: 'top' | 'bottom' | 'left' | 'right'; // Tooltip position (default: 'bottom')
onBeforeStep?: () => void | Promise; // Async action before showing step
};// Target types differ by platform:
// Web: CSS selector string (e.g., '[data-tour="welcome"]', '#my-button')
// React Native: String ID (registered via useTourTarget) or RefObject
type TourTarget = string | RefObject;
`Theming
Customize the look of the default tooltip and overlay without building custom components.
$3
`tsx
import {TourProvider, type TourTheme} from '@robertlinde/react-tour-kit/react';const theme: TourTheme = {
primaryColor: '#10b981', // Buttons, step badge, highlight border (default: '#3b82f6')
tooltipBackground: '#ffffff', // Tooltip background color (default: '#ffffff')
titleColor: '#1f2937', // Title text color (default: '#1f2937')
contentColor: '#4b5563', // Body text color (default: '#4b5563')
overlayColor: 'rgba(0, 0, 0, 0.5)', // Semi-transparent overlay (default: 'rgba(0, 0, 0, 0.5)')
};
function App() {
return (
);
}
`$3
Dark theme:
`tsx
const darkTheme: TourTheme = {
primaryColor: '#8b5cf6',
tooltipBackground: '#1f2937',
titleColor: '#f9fafb',
contentColor: '#d1d5db',
overlayColor: 'rgba(0, 0, 0, 0.7)',
};
`Brand colors:
`tsx
const brandTheme: TourTheme = {
primaryColor: '#your-brand-color',
};
// Other values use defaults
`$3
Theming works the same way in React Native:
`tsx
import {TourProvider, type TourTheme} from '@robertlinde/react-tour-kit/react-native';const theme: TourTheme = {
primaryColor: '#10b981',
overlayColor: 'rgba(0, 0, 0, 0.7)',
};
function App() {
return (
);
}
`---
Internationalization (i18n)
Customize all text labels and templates to support different languages or match your app's terminology.
$3
`tsx
import {TourProvider, type TourI18n} from '@robertlinde/react-tour-kit/react';const i18n: TourI18n = {
nextLabel: 'Next', // "Next" button text
prevLabel: 'Back', // "Back" button text
finishLabel: 'Finish', // Button text on last step (default: 'Finish' on web, 'Done' on native)
stepCounterTemplate: '{current} / {total}', // Step counter format (use {current} and {total} placeholders)
closeAriaLabel: 'Close tour', // Accessibility label for close button (web only)
dotAriaLabelTemplate: 'Go to step {step}', // Accessibility label template for step dots (web only)
};
function App() {
return (
);
}
`$3
Spanish:
`tsx
const spanishI18n: TourI18n = {
nextLabel: 'Siguiente',
prevLabel: 'Anterior',
finishLabel: 'Finalizar',
stepCounterTemplate: 'Paso {current} de {total}',
closeAriaLabel: 'Cerrar tour',
dotAriaLabelTemplate: 'Ir al paso {step}',
};
`German:
`tsx
const germanI18n: TourI18n = {
nextLabel: 'Weiter',
prevLabel: 'Zurück',
finishLabel: 'Fertig',
stepCounterTemplate: 'Schritt {current} von {total}',
closeAriaLabel: 'Tour schließen',
dotAriaLabelTemplate: 'Zu Schritt {step} gehen',
};
`Custom terminology:
`tsx
const customI18n: TourI18n = {
nextLabel: 'Continue',
prevLabel: 'Go Back',
finishLabel: 'Got it!',
stepCounterTemplate: 'Step {current} of {total}',
};
// Other values use defaults
`$3
i18n works the same way in React Native:
`tsx
import {TourProvider, type TourI18n} from '@robertlinde/react-tour-kit/react-native';const i18n: TourI18n = {
nextLabel: 'Siguiente',
prevLabel: 'Anterior',
finishLabel: 'Listo',
stepCounterTemplate: 'Paso {current} de {total}',
};
function App() {
return (
);
}
`$3
| Option | Web Default | React Native Default |
| ---------------------- | ----------------------- | ----------------------------- |
|
nextLabel | 'Next' | 'Next' |
| prevLabel | 'Back' | 'Back' |
| finishLabel | 'Finish' | 'Done' |
| stepCounterTemplate | '{current} / {total}' | 'Step {current} of {total}' |
| closeAriaLabel | 'Close tour' | N/A |
| dotAriaLabelTemplate | 'Go to step {step}' | N/A |---
Advanced Usage
$3
Run code before a step is shown. Useful for opening dialogs, switching tabs, navigating, etc.
`tsx
const steps: TourStep[] = [
{
target: '[data-tour="settings-tab"]',
title: 'Settings Tab',
content: 'Click here to access settings.',
},
{
target: '[data-tour="notifications-panel"]',
title: 'Notifications',
content: 'Configure your notification preferences here.',
onBeforeStep: async () => {
// Open the settings tab before showing this step
document.querySelector('[data-tour="settings-tab"]')?.click();
// Wait for the panel to appear
await new Promise((resolve) => setTimeout(resolve, 300));
},
},
];
`$3
Create your own tooltip with your design system.
Web:
`tsx
import {forwardRef} from 'react';
import {TourProvider, type TourTooltipProps} from '@robertlinde/react-tour-kit/react';const CustomTooltip = forwardRef(
({title, content, currentStep, totalSteps, position, isPositioned, onClose, onNext, onPrev}, ref) => (
ref={ref}
className="custom-tooltip"
style={{
position: 'fixed',
top: position.top,
left: position.left,
opacity: isPositioned ? 1 : 0,
}}
>
{title}
{content}
{currentStep + 1} of {totalSteps}
function App() {
return (
);
}
`
React Native:
`tsx
import {forwardRef} from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import {TourProvider, type TourTooltipProps} from '@robertlinde/react-tour-kit/react-native';
const CustomTooltip = forwardRef
({title, content, position, isPositioned, onNext, onClose}, ref) => (
style={{
position: 'absolute',
top: position.top,
left: position.left,
opacity: isPositioned ? 1 : 0,
backgroundColor: '#fff',
padding: 16,
borderRadius: 8,
}}
>
),
);
function App() {
return (
);
}
`
Customize the backdrop overlay.
Web:
`tsx
import {TourProvider, type TourOverlayProps} from '@robertlinde/react-tour-kit/react';
function CustomOverlay({highlightRect, onClose}: TourOverlayProps) {
return (
function App() {
return (
);
}
`
React Native:
`tsx
import {View, TouchableWithoutFeedback, StyleSheet} from 'react-native';
import {TourProvider, type TourOverlayProps} from '@robertlinde/react-tour-kit/react-native';
function CustomOverlay({highlightRect, onClose}: TourOverlayProps) {
return (
{/ Your custom overlay with cutout /}
position: 'absolute',
top: highlightRect.top - 4,
left: highlightRect.left - 4,
width: highlightRect.width + 8,
height: highlightRect.height + 8,
borderWidth: 2,
borderColor: '#your-brand-color',
borderRadius: 8,
}}
/>
);
}
function App() {
return (
);
}
`
Track which tours users have completed:
`tsx
import {TourProvider} from '@robertlinde/react-tour-kit/react';
function App() {
const handleTourEnd = async (tourId: string | null) => {
if (tourId) {
// Save to your backend
await fetch('/api/users/me/completed-tours/' + tourId, {
method: 'POST',
});
}
};
return (
);
}
`
Start tours based on user state:
`tsx
function TourTrigger({tourId, steps, autoStart = true}) {
const {startTour} = useTour();
const {user} = useUser(); // Your auth hook
useEffect(() => {
if (autoStart && user && !user.completedTours?.[tourId]) {
// Auto-start if user hasn't completed this tour
setTimeout(() => startTour(steps, tourId), 500);
}
}, [user, tourId, autoStart]);
return ;
}
`
The default components use inline styles for zero-dependency styling. You have three options for customization:
1. Theming: Use the theme prop to customize colors (see Theming section)TooltipComponent
2. Custom Components: Provide your own and OverlayComponent for full control
3. CSS Overrides (Web only): Override styles via CSS selectors
Web:
- Chrome, Firefox, Safari, Edge (latest versions)
React Native:
- iOS 13+
- Android 5.0+ (API 21+)
- React Native 0.70+
Requires React 18+.
Full TypeScript support is included. Import types as needed:
`tsx`
import type {
TourStep,
TourContextType,
TourProviderProps,
TourTooltipProps,
TourOverlayProps,
TourTheme,
TourI18n,
} from '@robertlinde/react-tour-kit/react';
The package exports utility functions for custom implementations:
`tsx`
import {
calculateTooltipPosition, // Calculate optimal tooltip position
findVisibleElement, // Find first visible element matching selector
} from '@robertlinde/react-tour-kit/react';
`bashInstall dependencies
npm install
MIT