Basic Example

This guide demonstrates a complete, production-ready tour implementation.

Full Application Example

Project Structure

app/
├── layout.tsx          # Root layout with TourProvider
├── page.tsx            # Home page with tour triggers
├── dashboard/
│   └── page.tsx        # Dashboard page (multi-page tour)
└── components/
    ├── TourSetup.tsx  # Tour initialization and setup
    └── TourButtons.tsx # Tour control buttons

1. Tour Configuration

// app/components/tour-config.ts
import { TourConfig } from 'Tourista';

export const appTours: TourConfig[] = [
  {
    id: 'onboarding',
    steps: [
      {
        id: 'welcome',
        page: '/',
        targetElement: '#logo',
        title: 'Welcome to Our App',
        content: "Let's take a quick tour to get you started.",
        canSkip: true,
      },
      {
        id: 'navigation',
        page: '/',
        targetElement: '#main-nav',
        title: 'Navigation Menu',
        content: 'Access all features from here.',
        canPrev: true,
        canSkip: true,
      },
      {
        id: 'dashboard-redirect',
        page: '/',
        title: 'Going to Dashboard',
        content: "Let's check out your dashboard...",
        autoAdvance: 2000, // Auto-advance after 2 seconds
      },
      {
        id: 'dashboard-overview',
        page: '/dashboard',
        targetElement: '#stats-widget',
        title: 'Your Statistics',
        content: 'Track your progress and metrics here.',
        canPrev: false, // Disable back button for this step
      },
      {
        id: 'complete',
        page: '/dashboard',
        title: 'Tour Complete!',
        content: "You're all set. Enjoy using the app!",
        targetElement: '#user-profile',
      },
    ],
  },
  {
    id: 'feature-tour',
    steps: [
      {
        id: 'new-feature',
        page: '/dashboard',
        targetElement: '#new-feature',
        title: 'New Feature Alert!',
        content: 'Check out our latest addition.',
      },
    ],
  },
];

2. Tour Provider Setup

// app/components/TourProvider.tsx
'use client';

import { TourProvider as TourProviderComponent, TourMachine } from 'Tourista';
import { appTours } from './tour-config';

export function TourProvider({ children }: { children: React.ReactNode }) {
  return (
    <TourProviderComponent tours={appTours}>
      <TourMachine
        onComplete={() => {
          localStorage.setItem('hasSeenTour', 'true');
          console.log('Tour completed!');
        }}
        onSkip={() => {
          localStorage.setItem('hasSeenTour', 'true');
          console.log('Tour skipped');
        }}
        closeOnClickOutside={true}
        overlayStyles={{
          opacity: 0.7,
          colorRgb: '0, 0, 0',
          padding: 8,
          radius: 8,
        }}
        cardPositioning={{
          floating: true,
          side: 'bottom',
          distancePx: 10,
        }}
      />
      {children}
    </TourProviderComponent>
  );
}

3. Root Layout Setup

// app/layout.tsx
import { TourProvider } from './components/TourProvider';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='en'>
      <body>
        <TourProvider>{children}</TourProvider>
      </body>
    </html>
  );
}

4. Auto-Start Tour Logic (Optional)

If you want to automatically start the tour for new users, create a component:

// app/components/AutoStartTour.tsx
'use client';

import { useEffect } from 'react';
import { useTourContext } from 'Tourista';

export function AutoStartTour() {
  const { startTour } = useTourContext();

  useEffect(() => {
    // Check if user is new (you'd implement this logic)
    const isNewUser = localStorage.getItem('hasSeenTour') !== 'true';

    if (isNewUser) {
      // Start tour after a short delay
      setTimeout(() => {
        startTour('onboarding');
      }, 1000);
    }
  }, [startTour]);

  return null; // This component doesn't render anything
}

5. Home Page Implementation

// app/page.tsx
'use client';

import { useTourContext } from 'Tourista';
import { AutoStartTour } from './components/AutoStartTour';

export default function HomePage() {
  const { startTour, isActive } = useTourContext();

  return (
    <>
      <AutoStartTour />

      <nav id='main-nav' className='bg-gray-800 text-white p-4'>
        <div className='flex items-center justify-between'>
          <h1 id='logo' className='text-2xl font-bold'>
            My App
          </h1>
          <button
            onClick={() => startTour('onboarding')}
            disabled={isActive}
            className='px-4 py-2 bg-blue-500 rounded disabled:opacity-50'
          >
            {isActive ? 'Tour Active' : 'Start Tour'}
          </button>
        </div>
      </nav>

      <main className='p-8'>
        <h2>Welcome to the Application</h2>
        <p>Click "Start Tour" to begin the onboarding process.</p>
      </main>
    </>
  );
}

6. Dashboard Page

// app/dashboard/page.tsx
'use client';

import { useTourContext } from 'Tourista';

export default function DashboardPage() {
  const { startTour } = useTourContext();

  return (
    <div className='p-8'>
      <div id='stats-widget' className='bg-white rounded-lg shadow p-6 mb-4'>
        <h2 className='text-xl font-bold mb-4'>Statistics</h2>
        <div className='grid grid-cols-3 gap-4'>
          <div>Total Users: 1,234</div>
          <div>Active: 456</div>
          <div>Growth: +12%</div>
        </div>
      </div>

      <div
        id='new-feature'
        className='bg-green-50 border border-green-200 rounded p-4 mb-4'
      >
        <h3>New Feature: Advanced Analytics</h3>
        <button
          onClick={() => startTour('feature-tour')}
          className='mt-2 px-3 py-1 bg-green-500 text-white rounded text-sm'
        >
          Learn More
        </button>
      </div>

      <div id='user-profile' className='flex items-center space-x-4'>
        <div className='w-12 h-12 bg-gray-300 rounded-full'></div>
        <div>
          <div className='font-semibold'>John Doe</div>
          <div className='text-sm text-gray-600'>[email protected]</div>
        </div>
      </div>
    </div>
  );
}

Hook Usage

useTourContext vs useTour

The library provides two hooks with different purposes:

useTourContext() - For managing tours:

  • Start a tour: startTour(tourId)

  • End the current tour: endTour()

  • Check if any tour is active: isActive

  • Get current tour config: tourConfig

useTour(tourId) - For controlling a specific active tour:

  • Requires a tourId parameter

  • Returns detailed state and controls for that specific tour

  • Access to step navigation: nextStep(), prevStep()

  • Send custom events: sendEvent()

  • Get current step data and progress

// Starting tours - use useTourContext
const { startTour } = useTourContext();
startTour('onboarding');

// Controlling active tour - use useTour
const tourControls = useTour('onboarding');
tourControls.nextStep();
console.log(tourControls.currentStepIndex);

Advanced Patterns

Custom Tour Card

// app/components/CustomTourCard.tsx
'use client';

import { CardProps } from 'Tourista';

export function CustomTourCard({
  title,
  content,
  currentStepIndex,
  totalSteps,
  canGoNext,
  canGoPrev,
  nextStep,
  prevStep,
  skipTour,
  canSkip = true,
}: CardProps) {
  return (
    <div className='bg-white rounded-lg shadow-xl p-6 max-w-sm'>
      {/* Progress indicator */}
      <div className='flex space-x-1 mb-4'>
        {Array.from({ length: totalSteps }).map((_, i) => (
          <div
            key={i}
            className={`h-1 flex-1 rounded ${
              i <= currentStepIndex ? 'bg-blue-500' : 'bg-gray-300'
            }`}
          />
        ))}
      </div>

      {/* Content */}
      <h3 className='text-lg font-semibold mb-2'>{title}</h3>
      <p className='text-gray-600 mb-4'>{content}</p>

      {/* Navigation */}
      <div className='flex justify-between items-center'>
        <div>
          {canSkip && (
            <button
              onClick={skipTour}
              className='text-gray-500 hover:text-gray-700'
            >
              Skip
            </button>
          )}
        </div>

        <div className='flex space-x-2'>
          {canGoPrev && (
            <button
              onClick={prevStep}
              className='px-3 py-1 border rounded hover:bg-gray-50'
            >
              Back
            </button>
          )}
          <button
            onClick={nextStep}
            className='px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600'
          >
            {canGoNext ? 'Next' : 'Finish'}
          </button>
        </div>
      </div>
    </div>
  );
}

// Usage - add to your TourProvider component
export function TourProvider({ children }: { children: React.ReactNode }) {
  return (
    <TourProviderComponent tours={appTours}>
      <TourMachine customCard={CustomTourCard} />
      {children}
    </TourProviderComponent>
  );
}

Async Step Example

const asyncTourConfig: TourConfig = {
  id: 'async-demo',
  steps: [
    {
      id: 'data-fetch',
      type: 'async',
      page: '/',
      content: {
        pending: {
          title: 'Loading Data',
          content: 'Fetching your information...',
        },
        processing: {
          title: 'Processing',
          content: 'Analyzing your data...',
        },
        success: {
          title: 'Data Ready!',
          content: 'Your data has been loaded successfully.',
          targetElement: '#data-display',
        },
      },
      events: {
        start: 'FETCH_USER_DATA',
        success: 'DATA_LOADED',
      },
    },
  ],
};

Best Practices

  1. Check First-Time Users: Use localStorage or a database flag to determine if a user needs onboarding

  2. Provide Skip Options: Always allow users to exit tours

  3. Keep Steps Concise: Each step should focus on one feature or action

  4. Use Step Options: Configure canPrev and canSkip per step for fine control

  5. Test Element Selectors: Ensure target elements exist before tour activation

  6. Handle Page Transitions: For multi-page tours, ensure smooth transitions

  7. Visual Progress: Show progress indicators to help users understand tour length

Next Steps

Last updated