This guide is based on a production implementation by Neta Dror ^___^ - Adapt the patterns to fit your specific needs.
This guide provides a step-by-step approach to implementing interactive walkthroughs/guided tours in React applications using Driver.js. This methodology can be adapted to any React project.
- Library Selection
- Implementation Steps
- Architecture Overview
- One-Shot Implementation Prompt
- Best Practices
When selecting a walkthrough library, consider:
| Criteria | Why It Matters |
|---|---|
| Bundle Size | Keep your app lightweight - aim for <10KB gzipped |
| License | MIT/Apache preferred for commercial use |
| Dependencies | Zero dependencies = fewer security vulnerabilities |
| Maintenance | Actively maintained = bug fixes and updates |
| Customization | Must support custom HTML/CSS in popovers |
| Framework Agnostic | Works with React via DOM refs, not React-specific |
Why Driver.js?
- ✅ ~5KB gzipped
- ✅ MIT License
- ✅ Zero dependencies
- ✅ Actively maintained
- ✅ Highly customizable (custom HTML/CSS in popovers)
- ✅ Framework agnostic (works with React, Vue, Angular, vanilla JS)
Installation:
npm install driver.js
npm install --save-dev @types/driver.js # If types are availableNote: Driver.js may not have official TypeScript types. You may need to create your own type definitions or use @types/driver.js if available.
Alternative Libraries:
- Intro.js - More features but heavier (~50KB)
- Shepherd.js - Good but requires more setup
- React Joyride - React-specific, good if you want React components
Create an implementation plan document (WALKTHROUGH_IMPLEMENTATION_PLAN.md):
- List all pages/components that need walkthroughs
- Categorize by priority (high/medium/low)
- Define user roles (if applicable) - which tours show for which users
- Document existing guide content - extract from modals/tooltips
- Create a checklist - track progress per page
Why this matters: Having a plan prevents scope creep and ensures consistency.
File: src/contexts/WalkthroughContext.tsx
Purpose: Global state management for all walkthroughs
Key Features:
- Track completion status per tour
- Persist to backend
- Provide start/stop/reset functions
- Handle sidebar/modal opening during tours
Type Definitions:
// src/types/walkthrough.ts
export interface WalkthroughStatuses {
[tourName: string]: boolean;
}
export interface WalkthroughContextType {
walkthroughStatuses: WalkthroughStatuses;
activeTour: string | null;
startWalkthrough: (tourName: string) => Promise<void>;
stopWalkthrough: () => void;
markComplete: (tourName: string) => Promise<void>;
resetAllWalkthroughs: () => Promise<void>;
getAllWalkthroughStatuses: () => WalkthroughStatuses;
isCompleted: (tourName: string) => boolean;
}Structure:
// Pseudo-code structure
interface WalkthroughContextType {
// State
walkthroughStatuses: Record<string, boolean>;
activeTour: string | null;
// Functions
startWalkthrough(tourName: string): Promise<void>;
stopWalkthrough(): void;
markComplete(tourName: string): Promise<void>;
resetAllWalkthroughs(): Promise<void>;
getAllWalkthroughStatuses(): Record<string, boolean>;
}File: src/hooks/useWalkthrough.ts
Purpose: Easy integration in any component
Type Definitions:
// src/types/walkthrough.ts
export interface UseWalkthroughReturn {
start: () => Promise<void>;
stop: () => void;
reset: () => Promise<void>;
isCompleted: boolean;
isActive: boolean;
steps: DriverStep[];
hasSteps: boolean;
}Usage:
const { start, stop, isCompleted, reset } = useWalkthrough('toolName');Benefits:
- Consistent API across all components
- Auto-loads step configuration
- Handles completion tracking
- Full TypeScript type safety
File: src/config/walkthroughSteps.ts
Purpose: Centralized step definitions
Type Definitions:
// src/types/walkthrough.ts
import { Step as DriverStep } from 'driver.js';
export type PopoverSide = 'top' | 'right' | 'bottom' | 'left' | 'center';
export type PopoverAlign = 'start' | 'center' | 'end';
export interface PopoverConfig {
title: string;
description: string;
side?: PopoverSide;
align?: PopoverAlign;
}
export interface WalkthroughStep extends Omit<DriverStep, 'popover'> {
element?: string;
popover?: PopoverConfig;
onHighlightStarted?: (element: Element) => void;
onNextClick?: () => void;
}
export interface WalkthroughSteps {
[tourName: string]: WalkthroughStep[];
}Structure:
import { WalkthroughSteps } from '../types/walkthrough';
export const walkthroughSteps: WalkthroughSteps = {
toolName: [
{
popover: {
title: 'Welcome',
description: 'Introduction text',
side: 'center',
align: 'center'
}
},
{
element: '[data-walkthrough="element-id"]',
popover: {
title: 'Feature Name',
description: 'Explanation',
side: 'bottom',
align: 'start'
}
}
]
};Key Points:
- Use
data-walkthroughattributes for element targeting - Left-align descriptions with lists/long content
- Use semantic HTML in descriptions for better formatting
- Full TypeScript type safety with Driver.js types
File: src/styles/walkthrough.css
Purpose: Match your app's design system
Key Customizations:
- Overlay color/opacity (soft grey recommended)
- Popover styling (match your modals/dialogs)
- Button colors (match primary brand color)
- Z-index management (ensure proper layering)
File: src/App.tsx
import { WalkthroughProvider } from './contexts/WalkthroughContext';
import './styles/walkthrough.css';
function App(): JSX.Element {
return (
<WalkthroughProvider>
{/* Your app */}
</WalkthroughProvider>
);
}
export default App;In your components:
<button data-walkthrough="start-button">
Start
</button>TypeScript Support:
// Extend React HTML attributes if needed
declare module 'react' {
interface HTMLAttributes<T> {
'data-walkthrough'?: string;
}
}Why data attributes?
- Non-intrusive (doesn't affect styling/functionality)
- Easy to identify tour targets
- Works with any element
- Type-safe with TypeScript declarations
Pattern:
import { useWalkthrough } from '../hooks/useWalkthrough';
interface MyComponentProps {
// Your component props
}
function MyComponent({}: MyComponentProps): JSX.Element {
const { start, isCompleted } = useWalkthrough('myTool');
return (
<div>
<h1>My Tool</h1>
<button onClick={start} className="start-tour-btn">
{isCompleted ? 'Replay Tour' : 'Start Tour'}
</button>
</div>
);
}
export default MyComponent;Placement:
- Next to page title (always visible)
- Inside guide modals (optional)
Styling:
- Match your primary button style
- Use icon + text for clarity
- Show "Replay" vs "Start" based on completion
Location: Header/navigation bar
Features:
- List all available tours
- Show completion status (checkmarks)
- Organize by category
- "Reset All" option
Benefits:
- Easy access to all tours
- Visual progress tracking
- Better UX than hidden tours
Sources:
- Existing guide modals
- Tooltips
- Documentation
- Help text
Why: Reuse content ensures consistency and saves time.
Best Practices:
- Start with welcome - No element, center popover
- Highlight key features - 3-5 main elements
- End with next steps - Guide users forward
- Keep it short - 5-8 steps max per page
Do:
- Use left-aligned text for lists
- Include emojis for visual interest
- Break long content into bullets
- Use semantic HTML (
<strong>,<ul>, etc.)
Don't:
- Write novels (keep it concise)
- Use jargon without explanation
- Assume prior knowledge
Challenge: Highlight sidebar items when sidebar is closed
Solution: Use onHighlightStarted callback to programmatically open sidebar before highlighting
onHighlightStarted: (element: Element): void => {
if (element.matches('[data-walkthrough="sidebar-item"]')) {
openSidebar();
}
}Type-Safe Implementation:
const handleHighlightStarted = (element: Element): void => {
if (element instanceof HTMLElement &&
element.matches('[data-walkthrough="sidebar-item"]')) {
openSidebar();
}
};Implementation:
- Check user role before showing tour
- Filter tour list in Help menu
- Conditionally render "Start Tour" buttons
Strategy:
- Main tool page = main tour
- Nested pages = separate tours
- Don't show nested tours in Help menu (only main tools)
- Access nested tours via "Start Tour" button on each page
- All tours start/stop correctly
- Completion status persists
- Reset functionality works
- Tours work on mobile
- Z-index layering correct (overlays don't block elements)
- Sidebar/modal opening works
- Content is clear and helpful
Issue: Overlay blocks highlighted element
- Fix: Increase z-index of highlighted element
Issue: Popover text not aligned
- Fix: Use
text-align: leftin CSS and wrap descriptions in<div>
Issue: Sidebar items not visible during tour
- Fix: Programmatically open sidebar before highlighting
Issue: Checkmarks hard to see on hover
- Fix: Use softer grey hover color (
#e8e8e8instead of#f8f8f8)
┌─────────────────────────────────────────┐
│ WalkthroughContext │
│ (Global State Management) │
│ - Completion tracking │
│ - Start/stop/reset functions │
│ - Backend persistence │
└──────────────┬──────────────────────────┘
│
├─── useWalkthrough Hook
│ (Per-component integration)
│
├─── walkthroughSteps Config
│ (Step definitions)
│
└─── Driver.js
(DOM manipulation & UI)
Data Flow:
- User clicks "Start Tour"
useWalkthroughhook callsstartWalkthrough('toolName')WalkthroughContextloads steps from config- Driver.js initializes and starts tour
- On completion, status saved to backend
- UI updates (checkmark appears, button changes)
Use this prompt with an AI assistant to get a complete implementation:
I want to implement interactive guided tours/walkthroughs for my React application. Here's what I need:
**Requirements:**
1. Use Driver.js library (lightweight, framework-agnostic)
2. Create a context provider for global state management
3. Create a reusable hook (useWalkthrough) for easy integration
4. Centralized step configuration file
5. Custom CSS to match my design system
6. Support for:
- Completion tracking (persist to backend)
- Role-based tours (optional)
- Dynamic sidebar/modal opening during tours
- Help menu listing all tours
- Reset functionality
**My App Details:**
- Framework: React [version] with TypeScript
- State Management: [Context API / Redux / etc.]
- Backend: [Firestore / REST API / etc.]
- UI Library: [Material-UI / Tailwind / etc.]
- Primary Color: [your color]
**Pages That Need Tours:**
[List your pages/components]
**Existing Content:**
[If you have guide modals/tooltips, mention them]
Please provide:
1. Complete WalkthroughContext.tsx with backend persistence and TypeScript types
2. useWalkthrough.ts hook with full type definitions
3. walkthroughSteps.ts structure with TypeScript interfaces (with examples)
4. walkthrough.css with customizable theme variables
5. Integration examples for App.tsx and a sample component with TypeScript
6. Help menu component (TypeScript)
7. Type definitions file (types/walkthrough.ts) with all interfaces
8. Best practices for content writing
9. Common issues and solutions
Make the code production-ready with:
- Full TypeScript type safety (no `any` types)
- Error handling with proper error types
- Type definitions for Driver.js if needed
- Comments explaining key decisions
- Proper React component typing (JSX.Element, React.FC, etc.)
Keep it concise:
- 1-2 sentences per step
- Use bullets for lists
- Left-align longer content
Be helpful:
- Explain "why" not just "what"
- Include next steps
- Link to detailed guides
Visual aids:
- Use emojis sparingly (1-2 per tour)
- Use
<strong>for emphasis - Structure with HTML (
<ul>,<ol>,<div>)
Performance:
- Lazy load Driver.js (code splitting)
- Don't initialize tours until needed
- Clean up event listeners on unmount
Accessibility:
- Ensure keyboard navigation works
- Add ARIA labels to tour buttons
- Test with screen readers
TypeScript Best Practices:
- Create a central
types/walkthrough.tsfile for all type definitions - Use
interfacefor object shapes,typefor unions/intersections - Avoid
any- useunknownif type is truly unknown - Use generic types for reusable patterns
- Export types from a single location for easy imports
- Use
as constfor literal types in step configs - Define proper return types for all functions (
Promise<void>,JSX.Element, etc.)
Example Type-Safe Step Config:
const stepConfig = {
toolName: [
{
popover: {
title: 'Welcome',
description: 'Introduction',
side: 'center' as const,
align: 'center' as const
}
}
]
} as const satisfies WalkthroughSteps;Maintainability:
- Keep step configs in one file
- Use consistent naming (
data-walkthrough="kebab-case") - Document complex callbacks
- Use TypeScript interfaces for type safety
- Create shared type definitions file
- Use type guards for runtime checks
Don't force tours:
- Always provide "Skip" option
- Make tours optional, not mandatory
- Allow replay anytime
Show progress:
- Display step counter ("2 of 5")
- Show completion status (checkmarks)
- Provide reset option
Respect user state:
- Don't show completed tours as "new"
- Remember user preferences
- Don't interrupt active workflows
Match your app:
- Use brand colors
- Match button styles
- Use same typography
- Follow spacing system
Visual hierarchy:
- Overlay should be subtle (not black)
- Popovers should stand out
- Highlighted elements should be clear
For a new implementation, follow this order:
-
Day 1: Planning
- List all pages needing tours
- Create implementation plan document
- Extract existing guide content
-
Day 2: Infrastructure
- Install Driver.js and TypeScript types
- Create type definitions file (types/walkthrough.ts)
- Create WalkthroughContext.tsx
- Create useWalkthrough.ts hook
- Create walkthroughSteps.ts config structure
- Create walkthrough.css
-
Day 3: Integration
- Add provider to App.tsx
- Add data attributes to 1-2 test pages
- Integrate hook in test pages (with TypeScript)
- Test basic tour flow
- Verify TypeScript compilation (no type errors)
-
Day 4: Content & Polish
- Write tour content for all pages
- Add "Start Tour" buttons
- Create Help menu
- Style to match design system
-
Day 5: Testing & Refinement
- Test all tours
- Fix z-index issues
- Test on mobile
- Get user feedback
src/
├── types/
│ └── walkthrough.ts (Type definitions)
├── contexts/
│ └── WalkthroughContext.tsx
├── hooks/
│ └── useWalkthrough.ts
├── config/
│ └── walkthroughSteps.ts
├── styles/
│ └── walkthrough.css
├── components/
│ └── Header.tsx (Help menu)
└── pages/
├── Homepage.tsx (with tour)
├── Tool1.tsx (with tour)
└── Tool2.tsx (with tour)
- Start with a plan - Document everything before coding
- Build infrastructure first - Context, hook, config, styles
- Use data attributes - Non-intrusive element targeting
- Reuse existing content - Don't rewrite guides
- Test thoroughly - Especially z-index and mobile
- Keep it simple - 5-8 steps per tour max
- Make it optional - Always allow skip/reset
If Driver.js doesn't have official types, create your own:
// src/types/driver.d.ts
declare module 'driver.js' {
export type PopoverSide = 'top' | 'right' | 'bottom' | 'left' | 'center';
export type PopoverAlign = 'start' | 'center' | 'end';
export interface PopoverConfig {
title?: string;
description?: string;
side?: PopoverSide;
align?: PopoverAlign;
showButtons?: boolean;
doneBtnText?: string;
closeBtnText?: string;
nextBtnText?: string;
prevBtnText?: string;
showProgress?: boolean;
progressText?: string;
}
export interface Step {
element?: string | Element;
popover?: PopoverConfig;
onHighlightStarted?: (element: Element) => void;
onHighlighted?: (element: Element) => void;
onDeselected?: (element: Element) => void;
onDestroyStarted?: (element: Element) => void;
onDestroyed?: (element: Element) => void;
}
export interface DriverOptions {
animate?: boolean;
opacity?: number;
padding?: number;
allowClose?: boolean;
overlayClickNext?: boolean;
doneBtnText?: string;
closeBtnText?: string;
nextBtnText?: string;
prevBtnText?: string;
showProgress?: boolean;
progressText?: string;
steps?: Step[];
onHighlightStarted?: (element: Element, step: Step) => void;
onHighlighted?: (element: Element, step: Step) => void;
onDeselected?: (element: Element, step: Step) => void;
onDestroyStarted?: (element: Element, step: Step) => void;
onDestroyed?: (element: Element, step: Step) => void;
onNextClick?: (element: Element, step: Step) => void;
onPrevClick?: (element: Element, step: Step) => void;
onCloseClick?: (element: Element, step: Step) => void;
onDestroyed?: () => void;
}
export default class Driver {
constructor(options?: DriverOptions);
drive(steps: Step[]): void;
moveNext(): void;
movePrevious(): void;
destroy(): void;
isActive(): boolean;
getActiveIndex(): number;
getActiveStep(): Step | null;
refresh(): void;
}
}Ensure your tsconfig.json includes:
{
"compilerOptions": {
"jsx": "react-jsx",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"skipLibCheck": true
},
"include": [
"src/**/*",
"src/types/**/*.d.ts"
]
}- Driver.js Documentation: https://driverjs.com/
- Driver.js GitHub: https://github.com/kamranahmedse/driver.js
- React Context API: https://react.dev/reference/react/useContext
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/intro.html
- React TypeScript Cheatsheet: https://react-typescript-cheatsheet.netlify.app/
This guide is based on a production implementation by Neta Dror ^___^ - Adapt the patterns to fit your specific needs.