Skip to content

robertlinde/react-tour-kit

Repository files navigation

react-tour-kit

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.

Features

  • 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)
  • Rich End Callbacks: Know why a tour ended (completed, closed, blur, escape, target-missing) with global and per-tour handlers
  • TypeScript First: Full TypeScript support

Installation

npm install @robertlinde/react-tour-kit

Live Demos

Try the demos locally:

# Web demo (React + React Router)
cd demos/react
npm install
npm run dev

# React Native demo (Expo + React Navigation)
cd demos/react-native
npm install
npm run ios  # or npm run android

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)

Quick Start

1. Wrap your app with TourProvider

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

function App() {
  return (
    <TourProvider>
      <YourApp />
    </TourProvider>
  );
}

2. Add data attributes to target elements

function Dashboard() {
  return (
    <div>
      <header data-tour="welcome">
        <h1>Dashboard</h1>
      </header>

      <nav data-tour="sidebar">{/* Navigation items */}</nav>

      <button data-tour="settings">Settings</button>
    </div>
  );
}

3. Start a tour

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 <button onClick={() => startTour(tourSteps, 'onboarding')}>Start Tour</button>;
}

Keyboard Navigation (Web)

Key Action
Arrow Right Next step
Arrow Left Previous step
Escape Close tour

React Native

Quick Start

1. Wrap your app with TourProvider

import {TourProvider} from '@robertlinde/react-tour-kit/react-native';

function App() {
  return (
    <TourProvider>
      <YourApp />
    </TourProvider>
  );
}

2. Register target elements with useTourTarget

import {View} from 'react-native';
import {useTourTarget} from '@robertlinde/react-tour-kit/react-native';

function MyComponent() {
  const welcomeRef = useTourTarget<View>('welcome-button');
  const settingsRef = useTourTarget<View>('settings-button');

  return (
    <View>
      <TouchableOpacity ref={welcomeRef}>
        <Text>Welcome</Text>
      </TouchableOpacity>

      <TouchableOpacity ref={settingsRef}>
        <Text>Settings</Text>
      </TouchableOpacity>
    </View>
  );
}

3. Start a tour

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 (
    <TouchableOpacity onPress={() => startTour(tourSteps, 'onboarding')}>
      <Text>Start Tour</Text>
    </TouchableOpacity>
  );
}

Using Refs Directly (React Native)

You can also pass refs directly instead of string IDs:

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 <TouchableOpacity ref={buttonRef}>...</TouchableOpacity>;
}

Navigation (React Native)

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

Web (React Router)

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 (
    <TourProvider>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </BrowserRouter>
    </TourProvider>
  );
}

// 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 <button onClick={() => startTour(steps, 'cross-page')}>Start Tour</button>;
}

React Native (React Navigation)

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 (
    <TourProvider>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Settings" component={SettingsScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </TourProvider>
  );
}

// Define steps with navigation
function HomeScreen() {
  const navigation = useNavigation();
  const {startTour} = useTour();
  const headerRef = useTourTarget<View>('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 (
    <View>
      <View ref={headerRef}>{/* ... */}</View>
      <Button onPress={() => startTour(steps, 'cross-screen')} title="Start Tour" />
    </View>
  );
}

function SettingsScreen() {
  const headerRef = useTourTarget<View>('settings-header');

  return <View ref={headerRef}>{/* ... */}</View>;
}

Important Notes for Cross-Page Tours

  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

TourProvider

The provider component that enables tour functionality throughout your app.

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

<TourProvider
  TooltipComponent={CustomTooltip} // Optional: custom tooltip component
  OverlayComponent={CustomOverlay} // Optional: custom overlay component
  theme={theme} // Optional: theme configuration
  onTourEnd={(tourId, info) => {
    // Optional: callback when tour ends
    // info.reason is one of: 'completed' | 'closed' | 'blur' | 'escape' | 'target-missing'
    console.log(`Tour ${tourId} ended: ${info.reason} at step ${info.stepIndex + 1}/${info.totalSteps}`);
  }}
>
  {children}
</TourProvider>;

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<TourTooltipProps> Custom tooltip component (must use forwardRef)
OverlayComponent ComponentType<TourOverlayProps> Custom overlay component
onTourEnd TourEndCallback Callback fired when any tour ends. Receives (tourId, info) with the reason and step context (see Tour End Callbacks)
closeOnOverlayClick boolean Whether clicking the overlay closes the tour. When false, users must click the ✕ button. Defaults to true

useTour Hook

Access the tour context from any component.

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, options?: StartTourOptions) => void
    endTour, // () => void
    nextStep, // () => void
    prevStep, // () => void
    goToStep, // (step: number) => void
  } = useTour();
}

TourStep

Define the steps in your tour.

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<void>; // 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<unknown>;

Tour End Callbacks

Tours can end in several ways, and callbacks receive context about why and where. You can register an end handler at two levels: globally on the provider, or per-tour via startTour.

Callback signature

type TourEndReason =
  | 'completed' // User clicked Finish on the last step
  | 'closed' // User clicked the close (✕) button, or `endTour()` was called programmatically
  | 'blur' // User clicked/tapped the overlay outside the highlighted element
  | 'escape' // User pressed Escape (web only)
  | 'target-missing'; // The step's target element could not be found and the tour had to end

type TourEndInfo = {
  reason: TourEndReason;
  stepIndex: number; // Zero-based index of the step the user was on
  step: TourStep; // The step object the user was on
  totalSteps: number; // Total number of steps in the tour
};

type TourEndCallback = (tourId: string | undefined, info: TourEndInfo) => void;

Note (React Native): the 'escape' reason only fires on web. On Android, the hardware back button is reported as 'closed'.

Provider-level callback

Use the provider-level onTourEnd for cross-cutting concerns that should run for every tour — analytics, logging, etc.

<TourProvider
  onTourEnd={(tourId, info) => {
    analytics.track('tour_ended', {
      tourId,
      reason: info.reason,
      lastStep: info.stepIndex + 1,
      totalSteps: info.totalSteps,
    });
  }}
>
  <App />
</TourProvider>

Per-tour callback

Pass an onTourEnd in the options argument to startTour when you need tour-specific end logic. This keeps the logic colocated with the tour that owns it, instead of dispatching on tourId in the provider callback.

function WelcomeButton() {
  const {startTour} = useTour();

  const handleStart = () => {
    startTour(onboardingSteps, 'onboarding', {
      onTourEnd: (tourId, info) => {
        if (info.reason === 'completed') {
          markOnboardingComplete();
        }
      },
    });
  };

  return <button onClick={handleStart}>Start Onboarding</button>;
}

Precedence

When both a provider-level and a per-tour callback are defined, the per-tour callback fires first, then the provider-level callback. Both always run on every end — the per-tour callback does not suppress the provider-level one.

// Provider:
onTourEnd = (id, info) => console.log('provider', id, info.reason);

// Tour:
startTour(steps, 'onboarding', {
  onTourEnd: (id, info) => console.log('tour', id, info.reason),
});

// On end, logs in order:
// 'tour'     'onboarding' 'completed'
// 'provider' 'onboarding' 'completed'

Reacting to specific reasons

Branch on info.reason to distinguish outcomes:

onTourEnd={(tourId, info) => {
  switch (info.reason) {
    case 'completed':
      markComplete(tourId);
      break;
    case 'blur':
    case 'closed':
    case 'escape':
      trackDismissal(tourId, info.reason, info.stepIndex);
      break;
    case 'target-missing':
      reportBug(`Tour ${tourId} broke at step ${info.stepIndex}: ${info.step.target}`);
      break;
  }
}}

Theming

Customize the look of the default tooltip and overlay without building custom components.

Theme Options

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 (
    <TourProvider theme={theme}>
      <YourApp />
    </TourProvider>
  );
}

Theme Examples

Dark theme:

const darkTheme: TourTheme = {
  primaryColor: '#8b5cf6',
  tooltipBackground: '#1f2937',
  titleColor: '#f9fafb',
  contentColor: '#d1d5db',
  overlayColor: 'rgba(0, 0, 0, 0.7)',
};

Brand colors:

const brandTheme: TourTheme = {
  primaryColor: '#your-brand-color',
};
// Other values use defaults

React Native

Theming works the same way in React Native:

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 (
    <TourProvider theme={theme}>
      <YourApp />
    </TourProvider>
  );
}

Internationalization (i18n)

Customize all text labels and templates to support different languages or match your app's terminology.

i18n Options

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 (
    <TourProvider i18n={i18n}>
      <YourApp />
    </TourProvider>
  );
}

i18n Examples

Spanish:

const spanishI18n: TourI18n = {
  nextLabel: 'Siguiente',
  prevLabel: 'Anterior',
  finishLabel: 'Finalizar',
  stepCounterTemplate: 'Paso {current} de {total}',
  closeAriaLabel: 'Cerrar tour',
  dotAriaLabelTemplate: 'Ir al paso {step}',
};

German:

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:

const customI18n: TourI18n = {
  nextLabel: 'Continue',
  prevLabel: 'Go Back',
  finishLabel: 'Got it!',
  stepCounterTemplate: 'Step {current} of {total}',
};
// Other values use defaults

React Native

i18n works the same way in React Native:

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 (
    <TourProvider i18n={i18n}>
      <YourApp />
    </TourProvider>
  );
}

Default Values

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

Async Step Actions

Run code before a step is shown. Useful for opening dialogs, switching tabs, navigating, etc.

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));
    },
  },
];

Custom Tooltip Component

Create your own tooltip with your design system.

Web:

import {forwardRef} from 'react';
import {TourProvider, type TourTooltipProps} from '@robertlinde/react-tour-kit/react';

const CustomTooltip = forwardRef<HTMLDivElement, TourTooltipProps>(
  ({title, content, currentStep, totalSteps, position, isPositioned, onClose, onNext, onPrev}, ref) => (
    <div
      ref={ref}
      className="custom-tooltip"
      style={{
        position: 'fixed',
        top: position.top,
        left: position.left,
        opacity: isPositioned ? 1 : 0,
      }}
    >
      <h3>{title}</h3>
      <p>{content}</p>
      <div className="tooltip-footer">
        <span>
          {currentStep + 1} of {totalSteps}
        </span>
        <button onClick={onPrev} disabled={currentStep === 0}>
          Back
        </button>
        <button onClick={onNext}>{currentStep === totalSteps - 1 ? 'Finish' : 'Next'}</button>
        <button onClick={onClose}>×</button>
      </div>
    </div>
  ),
);

function App() {
  return (
    <TourProvider TooltipComponent={CustomTooltip}>
      <YourApp />
    </TourProvider>
  );
}

React Native:

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<View, TourTooltipProps>(
  ({title, content, position, isPositioned, onNext, onClose}, ref) => (
    <View
      ref={ref}
      style={{
        position: 'absolute',
        top: position.top,
        left: position.left,
        opacity: isPositioned ? 1 : 0,
        backgroundColor: '#fff',
        padding: 16,
        borderRadius: 8,
      }}
    >
      <Text style={{fontWeight: 'bold'}}>{title}</Text>
      <Text>{content}</Text>
      <TouchableOpacity onPress={onNext}>
        <Text>Next</Text>
      </TouchableOpacity>
    </View>
  ),
);

function App() {
  return (
    <TourProvider TooltipComponent={CustomTooltip}>
      <YourApp />
    </TourProvider>
  );
}

Custom Overlay Component

Customize the backdrop overlay.

Web:

import {TourProvider, type TourOverlayProps} from '@robertlinde/react-tour-kit/react';

function CustomOverlay({highlightRect, onClose}: TourOverlayProps) {
  return (
    <div className="custom-overlay" onClick={onClose}>
      <div
        className="highlight-border"
        style={{
          position: 'absolute',
          top: highlightRect.top - 4,
          left: highlightRect.left - 4,
          width: highlightRect.width + 8,
          height: highlightRect.height + 8,
          border: '2px solid #your-brand-color',
          borderRadius: '8px',
        }}
      />
    </div>
  );
}

function App() {
  return (
    <TourProvider OverlayComponent={CustomOverlay}>
      <YourApp />
    </TourProvider>
  );
}

React Native:

import {View, TouchableWithoutFeedback, StyleSheet} from 'react-native';
import {TourProvider, type TourOverlayProps} from '@robertlinde/react-tour-kit/react-native';

function CustomOverlay({highlightRect, onClose}: TourOverlayProps) {
  return (
    <TouchableWithoutFeedback onPress={onClose}>
      <View style={StyleSheet.absoluteFill}>
        {/* Your custom overlay with cutout */}
        <View
          style={{
            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,
          }}
        />
      </View>
    </TouchableWithoutFeedback>
  );
}

function App() {
  return (
    <TourProvider OverlayComponent={CustomOverlay}>
      <YourApp />
    </TourProvider>
  );
}

Persisting Tour Completion

Track which tours users have fully completed. Only persist on 'completed' so dismissed or interrupted tours aren't marked done.

import {TourProvider, type TourEndCallback} from '@robertlinde/react-tour-kit/react';

function App() {
  const handleTourEnd: TourEndCallback = async (tourId, info) => {
    if (tourId && info.reason === 'completed') {
      // Save to your backend
      await fetch('/api/users/me/completed-tours/' + tourId, {
        method: 'POST',
      });
    }
  };

  return (
    <TourProvider onTourEnd={handleTourEnd}>
      <YourApp />
    </TourProvider>
  );
}

See Tour End Callbacks for the full list of end reasons and how to register per-tour end handlers.

Conditional Tour Start

Start tours based on user state:

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 <button onClick={() => startTour(steps, tourId)}>Start Tour</button>;
}

Styling

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)
  2. Custom Components: Provide your own TooltipComponent and OverlayComponent for full control
  3. CSS Overrides (Web only): Override styles via CSS selectors

Platform Support

Web:

  • Chrome, Firefox, Safari, Edge (latest versions)

React Native:

  • iOS 13+
  • Android 5.0+ (API 21+)
  • React Native 0.70+

Requires React 18+.

TypeScript

Full TypeScript support is included. Import types as needed:

import type {
  TourStep,
  TourContextType,
  TourProviderProps,
  TourTooltipProps,
  TourOverlayProps,
  TourTheme,
  TourI18n,
  TourEndCallback,
  TourEndInfo,
  TourEndReason,
  StartTourOptions,
} from '@robertlinde/react-tour-kit/react';

Utilities

The package exports utility functions for custom implementations:

import {
  calculateTooltipPosition, // Calculate optimal tooltip position
  findVisibleElement, // Find first visible element matching selector
} from '@robertlinde/react-tour-kit/react';

Development

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run start:dev

# Lint
npm run lint

# Format
npm run format

# Run web demo
cd demos/react && npm install && npm run dev

# Run native demo
cd demos/react-native && npm install && npm run ios

License

MIT

About

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.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors