React Joyride

repository·main·Indexed 27 days ago

https://github.com/gilbarbara/react-joyride

A library for creating guided product tours in React applications. It allows developers to highlight UI elements using CSS selectors, React refs, or HTMLElements to provide contextual information to users. The library provides a <Joyride> component for standard implementations and a useJoyride hook for fine-grained control over tour state and custom UI components. It supports both controlled and uncontrolled modes, custom lifecycle hooks (before/after), and customizable UI components for beacons, tooltips, and loaders.

Tokens
32.4K
Snippets
82
Records
146
Agent score
89%

What's inside react-joyride

  1. Use the React Joyride public APIs

    main

    React Joyride provides two primary ways to implement a tour:

    1. <Joyride> component: An SSR-safe wrapper component that manages the tour lifecycle internally.
    2. useJoyride(props) hook: A hook that returns the tour state and control methods. It returns an object containing { controls, failures, on, state, step, Tour }.

    Use the component for standard implementations and the hook when you need fine-grained control over the tour state or want to build custom UI components.

  2. Understand React Joyride Props structure

    main

    React Joyride props are categorized into two main groups:

    1. Tour Props: Control the tour lifecycle, such as starting/stopping the tour, handling events, and managing controlled vs. uncontrolled modes.
    2. Shared Props: Configure visual and interactive elements like components, positioning, locale, and styles. These are applied globally to the tour but can be overridden on individual steps.

    Note: The only required prop is steps, which is an array of Step objects.

  3. Understand Tour Status transitions

    main

    Joyride operates as a state machine with several statuses. Understanding these transitions helps you manage the tour's lifecycle programmatically.

    Tour Statuses

    • idle: Initial state. No tour running.
    • waiting: Tour started but no steps are loaded yet. Transitions to running once steps are available.
    • ready: Steps are loaded, tour is ready to start.
    • running: Tour is active and displaying steps.
    • paused: Tour was stopped mid-way via controls.stop(). Can be resumed.
    • finished: All steps completed.
    • skipped: User clicked Skip to exit early.

    Key Transitions

    • Starting: Setting run={true} or calling controls.start() moves the tour to running (or waiting if steps aren't loaded). Calling controls.start() from a paused state resumes the tour.
    • Stopping: Setting run={false} or calling controls.stop() moves the tour to paused. Using the Skip button or controls.skip() moves it to skipped. Completing the last step or clicking Close on the final step moves it to finished.
    • Resetting: controls.reset() moves the tour to ready. controls.reset(true) restarts the tour immediately in the running state.
  4. Understand Step Lifecycle phases

    main

    Each individual step in a tour progresses through a specific lifecycle. This is critical for timing animations or data fetching.

    Step Lifecycle Phases

    • init: Target lookup. The before hook runs here if defined.
    • ready: Target is found and visible.
    • beacon_before: Scroll to target and calculate positioning. (Skipped if skipBeacon is true, placement is center, or in continuous mode with Next/Prev).
    • beacon: The beacon is displayed and waiting for user interaction.
    • tooltip_before: Scroll to target and calculate positioning.
    • tooltip: The tooltip is displayed and interactive.
    • complete: The after hook fires and the tour advances.

    Note: The beacon phases are skipped when skipBeacon is set, placement is center, or in continuous tours navigating with Next/Prev.

  5. Understand the Joyride Event Sequence

    main

    Joyride fires events via the onEvent callback as a step progresses. A typical sequence for a single step is:

    1. tour:start
    2. step:before_hook (only if the step has a before hook)
    3. step:before
    4. scroll:start (only if scrolling is required)
    5. scroll:end
    6. beacon (skipped in continuous mode with Next/Prev)
    7. tooltip
    8. step:after
    9. step:after_hook (only if the step has an after hook)
    10. ... next step ...
    11. tour:end

    Error events like error:target_not_found or error can fire at any point if a target is missing or a hook fails.

  6. Implement a Custom Tooltip Component

    main

    To customize the appearance of the tour tooltips, provide a component to the tooltipComponent prop in Joyride or the useJoyride hook. The component receives TooltipRenderProps which includes access to the current step, navigation props (backProps, closeProps, primaryProps, skipProps), and metadata (index, isLastStep, size).

    import type { TooltipRenderProps } from 'react-joyride';
    
    function CustomTooltip({
      backProps,
      closeProps,
      index,
      isLastStep,
      primaryProps,
      size,
      skipProps,
      step,
      tooltipProps,
    }: TooltipRenderProps) {
      return (
        <div
          {...tooltipProps}
          style={{
            background: '#fff',
            borderRadius: 8,
            maxWidth: 400,
            padding: 20,
            width: step.width,
          }}
        >
          {step.title && <h3 style={{ margin: '0 0 8px' }}>{step.title}</h3>}
          <div>{step.content}</div>
    
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 16 }}>
            {step.buttons.includes('skip') && !isLastStep && (
              <button {...skipProps} type="button">Skip</button>
            )}
            <div style={{ display: 'flex', gap: 8, marginLeft: 'auto' }}>
              {index > 0 && (
                <button {...backProps} type="button">Back</button>
              )}
              <button {...primaryProps} type="button">
                {isLastStep ? 'Done' : `Next (${index + 1}/${size})`}
              </button>
            </div>
          </div>
        </div>
      );
    }
    
    // Usage:
    // <Joyride tooltipComponent={CustomTooltip} ... />
    // or in useJoyride: useJoyride({ tooltipComponent: CustomTooltip, ... })
  7. Use center placement for modal-style tooltips

    main

    Setting the placement to 'center' and the target to 'body' allows you to create a centered modal overlay. When using center placement, the beacon and arrow are automatically hidden.

    {
      target: 'body',
      placement: 'center',
      content: (
        <div
          >
          <h2>Welcome!</h2>
          <p>This appears as a centered modal overlay.</p>
        </div>
      ),
      // Center placement automatically hides beacon and arrow
    }
  8. Configure step styles and adaptive width

    main

    React Joyride uses a getStyles(step) utility to manage visual presentation. It merges your provided step styles with library defaults.

    Key behavior:

    • Adaptive Width: The tooltip width is automatically calculated as min(step.width, window.innerWidth - 30) to ensure it does not overflow the viewport.
    • Style Objects: The utility generates style objects for various elements including beacon, buttons, tooltip, overlay, and arrow.
    • Overrides: User-provided styles in the step object take precedence over defaults.
  9. Use Controlled mode for Joyride tours

    main

    Controlled mode is used when an external system must own the stepIndex (e.g., a persistence layer that resumes a tour on reload, or a parent orchestrator).

    Warning: Do not use controlled mode just for conditional UI (like opening a sidebar). Instead, use the before step hook. Driving stepIndex via useEffect can desynchronize the lifecycle and break overlay/keyboard handlers.

    To implement controlled mode, you must manage the stepIndex yourself and update it within the onEvent callback.

    const [stepIndex, setStepIndex] = useState(0);
    const [run, setRun] = useState(false);
    
    <Joyride
      continuous
      run={run}
      stepIndex={stepIndex}
      steps={steps}
      onEvent={(data, controls) => {
        const { action, index, status, type } = data;
    
        if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
          setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1));
        } else if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) {
          setRun(false);
        }
      }}
    />
  10. Implement Uncontrolled vs Controlled Tours

    main

    By default, the tour manages its own navigation. Use before and after hooks to handle asynchronous UI changes (like opening a dropdown or waiting for an animation). The library automatically waits for the before promise to resolve.

    Controlled Mode

    Use this sparingly when you must sync the stepIndex with external state (e.g., URL parameters).

    Rules for Controlled Mode:

    • go() and reset() are disabled.
    • You must manually update stepIndex in response to events (like step:after).
    • The tour pauses at COMPLETE; you must manually advance it.
    • You must provide the stepIndex prop to useJoyride or <Joyride>.
    // Controlled Mode Example
    const [stepIndex, setStepIndex] = useState(0);
    const [run, setRun] = useState(true);
    
    const { Tour } = useJoyride({
      continuous: true,
      run,
      stepIndex,  // This makes it controlled
      steps,
      onEvent: (data) => {
        const { action, index, status, type } = data;
    
        if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(status)) {
          setRun(false);
          return;
        }
    
        if (type === 'step:after' || type === 'error:target_not_found') {
          setStepIndex(index + (action === 'prev' ? -1 : 1));
        }
      },
    });
  11. Import React Joyride modules

    main

    React Joyride uses named exports. There is no default export in v3.

    // Named exports
    import { Joyride, useJoyride } from 'react-joyride';
    
    // Constants for type-safe comparisons
    import { ACTIONS, EVENTS, LIFECYCLE, ORIGIN, STATUS } from 'react-joyride';
    
    // Types
    import type { Step, Props, EventData, Controls, TooltipRenderProps } from 'react-joyride';
  12. Quick Start with the Joyride component

    main

    To implement a basic guided tour, use the Joyride component. The only required prop is steps. You can use the run prop to automatically start the tour when the component mounts, or control it via other props. Use continuous to allow users to progress through steps using navigation buttons.

    import { Joyride } from 'react-joyride';
    
    const steps = [
      { content: 'This is my awesome feature!', target: '.my-first-step' },
      { content: 'This is another awesome feature!', target: '.my-other-step' },
    ];
    
    export default function App() {
      return (
        <div>
          <Joyride continuous run={true} steps={steps} />
          {/* your app content */}
        </div>
      );
    }