@reactour/tour vs react-joyride vs reactour
React Tour Libraries
@reactour/tourreact-joyridereactour

React Tour Libraries

React tour libraries are designed to help developers create guided tours for their applications, enhancing user onboarding and experience. These libraries provide a structured way to introduce users to the features of an application through interactive tooltips and modals. They allow developers to highlight specific elements, provide contextual information, and guide users through complex workflows, ultimately improving user engagement and retention.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@reactour/tour04,08398.5 kB82a year agoMIT
react-joyride07,768719 kB0a month agoMIT
reactour04,083120 kB822 years agoMIT

Feature Comparison: @reactour/tour vs react-joyride vs reactour

Customization

  • @reactour/tour:

    @reactour/tour offers extensive customization options, allowing developers to define their own styles, animations, and behaviors for tour steps. This flexibility enables the creation of unique user experiences that align with the application's design language.

  • react-joyride:

    react-joyride provides a rich set of built-in customization options, including predefined styles, themes, and the ability to control the appearance and behavior of each step. This makes it easy to create visually appealing tours that can be tailored to fit the application's branding.

  • reactour:

    reactour is designed for simplicity, offering basic customization options such as step positioning and tooltip content. While it may not be as feature-rich as the other libraries, it allows for quick adjustments to fit the application's needs.

Ease of Use

  • @reactour/tour:

    @reactour/tour has a straightforward API that is easy to integrate into existing React applications. Its component-based approach makes it intuitive for developers familiar with React, allowing for rapid development and deployment of tours.

  • react-joyride:

    react-joyride is user-friendly and provides a comprehensive documentation that guides developers through the setup process. Its extensive features may require some learning, but the library is designed to be accessible even for those new to creating guided tours.

  • reactour:

    reactour is the easiest to set up and use among the three options. Its minimalistic approach allows developers to quickly implement basic tours without a steep learning curve, making it ideal for projects with tight deadlines.

Performance

  • @reactour/tour:

    @reactour/tour is optimized for performance, ensuring that the rendering of tour steps does not significantly impact the application's overall performance. It uses React's lifecycle methods efficiently to manage updates and rendering.

  • react-joyride:

    react-joyride is designed with performance in mind, but its extensive features may introduce some overhead. However, it provides options to optimize performance, such as controlling the rendering of steps based on user interactions.

  • reactour:

    reactour is lightweight and has minimal overhead, making it a performant choice for applications that require basic tour functionality without the need for complex features.

Community and Support

  • @reactour/tour:

    @reactour/tour has a growing community and is actively maintained, providing developers with access to resources, updates, and community support. Its documentation is clear and helpful for troubleshooting.

  • react-joyride:

    react-joyride boasts a large community and extensive documentation, making it easy to find support and examples. The library is well-maintained, with regular updates and improvements based on user feedback.

  • reactour:

    reactour has a smaller community compared to the others, but it is still maintained and offers basic documentation. Developers may find fewer resources available for troubleshooting compared to more popular libraries.

Integration

  • @reactour/tour:

    @reactour/tour integrates seamlessly with React applications, allowing for easy incorporation into existing projects. It works well with other libraries and frameworks, enhancing its versatility.

  • react-joyride:

    react-joyride also integrates well with React and can be used alongside other libraries. Its extensive features make it suitable for complex applications that require detailed tours.

  • reactour:

    reactour is designed for quick integration into React projects, making it a good choice for developers looking for a simple solution without extensive setup.

How to Choose: @reactour/tour vs react-joyride vs reactour

  • @reactour/tour:

    Choose @reactour/tour if you need a highly customizable and flexible tour solution that integrates well with React's component architecture. It offers a simple API and is designed for developers who want to create tailored user experiences without being constrained by predefined styles or behaviors.

  • react-joyride:

    Select react-joyride if you prefer a feature-rich library that provides a comprehensive set of built-in options for creating tours. It includes various customization features, such as step positioning, scrolling behavior, and the ability to pause and resume tours, making it ideal for more complex onboarding scenarios.

  • reactour:

    Opt for reactour if you are looking for a lightweight and straightforward library that allows for quick implementation of tours with minimal setup. It is suitable for projects that require basic tour functionality without extensive customization.

README for @reactour/tour

Reactour

Tourist Guide into your React Components

Documentation

https://docs.react.tours

This documentation is for the latest release, which uses npm scoped package @reactour. The original reactour is now on branch v1 and its documentation can be found here.

Install

npm i -S @reactour/tour
# or
yarn add @reactour/tour

Usage

Add the TourProvider at the root of your Application, passing the steps of the elements to highlight during the Tour.

// ...
import { TourProvider } from '@reactour/tour'

ReactDOM.render(
  <TourProvider steps={steps}>
    <App />
  </TourProvider>,
  document.getElementById('root')
)

const steps = [
  {
    selector: '.first-step',
    content: 'This is my first Step',
  },
  // ...
]

Then somewhere down the Application tree, control the Tour using useTour hook.

import { useTour } from '@reactour/tour'

function App() {
  const { setIsOpen } = useTour()
  return (
    <>
      <p className="first-step">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent at
        finibus nulla, quis varius justo. Vestibulum lorem lorem, viverra porta
        metus nec, porta luctus orci
      </p>
      <button onClick={() => setIsOpen(true)}>Open Tour</button>
    </>
  )
}

Examples

Playground

The Playground is the perfect place to play aroud with all @reactour Components. Here is an online version.

Sandboxes

Edit @reactour/tour Demo Template

Feel free to make a PR proposing new sandboxes or demos to add in the playground.

TourProvider

steps?: StepType[]

Array of elements to highlight with special info and props.

StepType

selector: string | Element

A string containing one CSS Selector to match and highlight the element at the time of this step.

content: string | ({ setCurrentStep, transition, isHighlightingObserved, currentStep, setIsOpen }) => void

The content to show inside the Popover at the time of this step. Using a function have parameters to use inside content.

position?: 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number]

The preferred postion to position the Popover in relation with the highlighted element. Will be automatically calculated in case of unavailable space.

highlightedSelectors?: string[]

Array of CSS Selector to be included (by union) in the highlighted region of the Mask.

mutationObservables?: string[]

Array of CSS Selector that addition or removal will triggered a rerender of the Mask shape.

resizeObservables?: string[]

Array of CSS Selector that when resizeing each will triggered a rerender of the Mask shape.

navDotAriaLabel?: string

String to assign to aria-label attribute of the Dot of this step.

stepInteraction?: boolean

Allow to reenable the interaction for this specific step, when disableInteraction (from TourProvider) is true.

action?: (elem: Element | null) => void

Action fired when the Tour arrives in this step.

actionAfter?: (elem: Element | null) => void

Action fired when the Tour leaves this step.

disableActions?: boolean

Allow to disable all possible actions (interaction with Mask, Navigation Arrows, Navigation Dots, Close button and keyboard events) when the Tour is in this step.

padding?: Padding

Control padding spaces for this specific step.

bypassElem?: boolean

Excludes the main selector when calculating highlited area if present highlightedSelectors.

styles?: StylesObj & PopoverStylesObj & MaskStylesObj

Customize styles fro this specific step.

components?: PopoverComponentsType

Prop to customize granurally each Component inside the Popover.

Components available

keyprops
Badgestyles
Closestyles, onClick, disabled
Contentcontent,setCurrentStep,transition, isHighlightingObserved,currentStep,setIsOpen
Navigationstyles,setCurrentStep, steps, currentStep, disableDots, nextButton, prevButton, setIsOpen, hideButtons, hideDots, disableAll, rtl, Arrow,
Arrowstyles, inverted, disabled
Example
import { components } from '@reactour/tour'

function Badge({ children }) {
  return (
    <components.Badge
      styles={{ badge: (base) => ({ ...base, backgroundColor: 'red' }) }}
    >
      👉 {children} 👈
    </components.Badge>
  )
}

function Close({ onClick }) {
  return (
    <button
      onClick={onClick}
      style={{ position: 'absolute', right: 0, top: 0 }}
    >
      x
    </button>
  )
}

const steps = [
  /* ... */
]

export default function App() {
  return (
    <TourProvider steps={steps} components={{ Badge, Close }}>
      {/* ... */}
    </TourProvider>
  )
}

styles?: StylesObj & PopoverStylesObj & MaskStylesObj

Prop to customize styles for the different parts of the Mask, Popover and Tour using a function that allows to extend the base styles an take advantage of some state props.

Style keys and props available

Refer to Mask docs and Popover docs for its specific Components

Tour Components
keyprops
badge
controls
buttondisabled
arrowdisabled
dotcurrent, disabled, showNumber
closedisabled
Example
const styles = {
  maskWrapper: (base) => ({
    ...base,
    color: 'red',
  }),
  highlightedArea: (base, { x, y }) => ({
    ...base,
    x: x + 10,
    y: y + 10,
  }),
  badge: (base) => ({ ...base, color: 'blue' }),
}

padding?: Padding

Type details
type Padding =
  | number
  | {
      mask?: ComponentPadding
      popover?: ComponentPadding
      wrapper?: ComponentPadding
    }

// x and y same value or [x, y] or [top, x, bottom] or [top, right, bottom, left]
type ComponentPadding = number | number[]

Extra space to add between the Mask and the Popover and the highlighted element. A single number coordinates both spaces. Otherwise, passing an Object specifying the Component space.

position?: Position

Type details
type Position =
  | 'top'
  | 'right'
  | 'bottom'
  | 'left'
  | 'center'
  | [number, number]
  | ((postionsProps: PositionProps) => Position)

type PositionProps = {
  bottom: number
  height: number
  left: number
  right: number
  top: number
  width: number
  windowWidth: number
  windowHeight: number
}

Set a global position for the Popover in all steps, fixed in case of [number, number], calculated in case of position string

setCurrentStep: Dispatch<React.SetStateAction<number>>

Function to control the Tour current step state.

currentStep: number

Custom Tour current step state.

This option could be overrided on specific steps using stepInteraction prop.

disableInteraction?: boolean | ((clickProps: Pick<ClickProps, 'currentStep' | 'steps' | 'meta'>) => boolean)

Disables the ability to click or interact in any way with the Highlighted element on every step.

This option could be overrided on specific steps using stepInteraction prop.

disableFocusLock?: boolean

The Tour uses FocusScope in order to lock the focus iteration inside the Popover when Tour is active. This prop allows to disable this behaviour.

disableDotsNavigation?: boolean

Disable interactivity with Dot navigation inside Popover.

disableWhenSelectorFalsy?: boolean

If true, don't show tours when selector or document.getElementById(step.selector) is falsy.

disableKeyboardNavigation?: boolean | KeyboardParts[]

Type details
type KeyboardParts = 'esc' | 'left' | 'right'

Disable all keyboard navigation events when true, disable only selected keys when array.

default: false

className?: string

Class assigned to Popover.

default: reactour__popover

maskClassName?: string

Class assigned to Mask.

default: reactour__mask

highlightedMaskClassName?: string

Class assigned to highlighted part of Mask. Useful when using disableInteraction.

nextButton?: (props: BtnFnProps) => void

prevButton?: (props: BtnFnProps) => void

Type details
type BtnFnProps = {
  Button: React.FC<NavButtonProps>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  stepsLength: number
  currentStep: number
  setIsOpen: Dispatch<React.SetStateAction<boolean>>
}

type NavButtonProps = {
  onClick?: () => void
  kind?: 'next' | 'prev'
  hideArrow?: boolean
}

Helper functions to customize the Next and Prev buttons inside Popover, with useful parameters. It is possible to use the base Button and customize the props.

afterOpen?: (target: Element | null) => void

Action fired just after the Tour is open.

beforeClose?: (target: Element | null) => void

Action fired just before the Tour is closed.

onClickMask?: (clickProps: ClickProps) => void

Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

Function that overrides the default close behavior of the Mask click handler. Comes with useful parameters to play with.

onClickClose?: (clickProps: ClickProps) => void

Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

Function that overrides the default close behavior of the Close icon click handler. Comes with useful parameters to play with.

onClickHighlighted?: (e: MouseEventHandler<SVGRectElement>, clickProps: ClickProps) => void

Click handler for highlighted area. Only works when disableInteraction is active. Useful in case is needed to avoid onClickMask when clicking the highlighted element.

Example
<TourProvider
  steps={steps}
  disableInteraction
  onClickHighlighted={(e, clickProps) => {
    console.log('No interaction at all')
    if (clickProps.currentStep < 2) {
      e.stopPropagation()
      event.preventDefault()
      clickProps.setCurrentStep(
        Math.min(clickProps.currentStep + 1, clickProps.steps.length - 1)
      )
    }
  }}
>
  {/* ... */}
</TourProvider>
Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

keyboardHandler?: KeyboardHandler

Function to handle keyboard events in a custom way.

Type details
type KeyboardHandler = {
  keyboardHandler?: (
    e: KeyboardEvent,
    clickProps?: ClickProps,
    status?: {
      isEscDisabled?: boolean
      isRightDisabled?: boolean
      isLeftDisabled?: boolean
    }
  ) => void
}
Example
<TourProvider
  steps={steps}
  disableInteraction
  keyboardHandler={(e, clickProps) => {
    if (e.key === 'ArrowRight') {
      clickProps.setCurrentStep(
        Math.min(clickProps.currentStep + 1, clickProps.steps.length - 1)
      )
    }
    if (e.key === 'ArrowLeft') {
      clickProps.setCurrentStep(Math.max(clickProps.currentStep - 1, 0))
    }
    if (e.key === 'Escape') {
      const nextStep = Math.floor(Math.random() * clickProps.steps.length)
      clickProps.setCurrentStep(nextStep)
    }
  }}
>
  {/* ... */}
</TourProvider>

badgeContent?: (badgeProps: BadgeProps) => any

Type details
type BadgeProps = {
  totalSteps: number
  currentStep: number
  transition: boolean
}

Function to customize the content of the Badge using helper parameters like the current and total steps and if the Tour is transitioning between steps.

showNavigation?: boolean

Show or hide the Navigation (Prev and Next buttons and Dots) inside Popover.

showPrevNextButtons?: boolean

Show or hide Prev and Next buttons inside Popover.

showCloseButton?: boolean

Show or hide the Close button inside Popover.

showBadge?: boolean

Show or hide the Badge inside Popover.

showDots?: boolean

Show or hide dots navigation inside Popover.

scrollSmooth?: boolean

Activate smooth scroll behavior when steps are outside viewport.

default: false

inViewThreshold?: { x?: number, y?: number } | number

Tolerance in pixels to add when calculating if the step element is outside viewport to scroll into view.

accessibilityOptions?: A11yOptions

Type details
type A11yOptions = {
  ariaLabelledBy: string
  closeButtonAriaLabel: string
  showNavigationScreenReaders: boolean
}

Configure generic accessibility related attributes like aria-labelledby, aria-label for Close button and if show or hide Dot navigation in screen readers.

rtl?: boolean

Option to navigate and show Navigation in right-to-left mode

maskId?: string

Mask ID to pass directly into the Mask component

clipId?: string

Clip ID to pass directly into the Mask component

onTransition?: PositionType

Function to control the behavior of Popover when is transitioning/scrolling from one step to another, calculating with Popover next position and previous one

Type details
type PositionType = (
  postionsProps: PositionProps,
  prev: RectResult
) => 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number]

ContentComponent?: ComponentType<PopoverContentProps>

Completelly custom component to render inside the Popover.

Type details
type PopoverContentProps = {
  styles?: StylesObj & PopoverStylesObj & MaskStylesObj
  badgeContent?: (badgeProps: BadgeProps) => any
  components?: PopoverComponentsType
  accessibilityOptions?: A11yOptions
  disabledActions?: boolean
  onClickClose?: (clickProps: ClickProps) => void
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  currentStep: number
  transition?: boolean
  isHighlightingObserved?: boolean
  setIsOpen: Dispatch<React.SetStateAction<boolean>>
  steps: StepType[]
  showNavigation?: boolean
  showPrevNextButtons?: boolean
  showCloseButton?: boolean
  showBadge?: boolean
  nextButton?: (props: BtnFnProps) => void
  prevButton?: (props: BtnFnProps) => void
  disableDotsNavigation?: boolean
  rtl?: boolean
}
Example
function ContentComponent(props) {
  const isLastStep = props.currentStep === props.steps.length - 1
  const content = props.steps[props.currentStep].content
  return (
    <div style={{ border: '5px solid red', padding: 10, background: 'white' }}>
      {/* Check if the step.content is a function or a string */}
      {typeof content === 'function'
        ? content({ ...props, someOtherStuff: 'Custom Text' })
        : content}
      <button
        onClick={() => {
          if (isLastStep) {
            props.setIsOpen(false)
          } else {
            props.setCurrentStep((s) => s + 1)
          }
        }}
      >
        {isLastStep ? 'x' : '>'}
      </button>
    </div>
  )
}

const steps = [
  /* ... */
]

function App() {
  return (
    <TourProvider
      steps={steps}
      ContentComponent={ContentComponent}
      styles={{ popover: (base) => ({ ...base, padding: 0 }) }}
    >
      {/* ... */}
    </TourProvider>
  )
}

Wrapper?: ComponentType

Element which wraps the Tour, useful in case is needed to port the Tour into a Portal. Defaults to React.Fragment

useTour

Later in any Component down in the tree of TourProvider you can control the Tour in many ways

import { useTour } from '@reactour/tour'

function MyComponent() {
  const { isOpen, currentStep, steps, setIsOpen, setCurrentStep, setSteps } = useTour()
  return (
    <>
      <h1>{isOpen ? 'Welcome to the tour!' : 'Thank you for participate!'}</h1>
      <p>
        Now you are visiting the place {currentStep + 1} of {steps.length}
      </p>
      <nav>
        <button onClick={() => setIsOpen(o => !o)}>Toggle Tour</button>
        <button onClick={() => setCurrentStep(3)}>
          Take a fast way to 4th place
        </button>
        <button
          onClick={() =>
            setSteps([
              { selector: '.new-place-1', content: 'New place 1' },
              { selector: '.new-place-2', content: 'New place 2' },
            ])
            setCurrentStep(1)
          }
        >
          Switch to a new set of places, starting from the last one!
        </button>
      </nav>
    </>
  )
}

isOpen: boolean

Is the Tour open or close

currentStep: number

The current step. zero based

steps: StepType[]

The Array of steps set currently

setIsOpen: Dispatch<React.SetStateAction<boolean>>

SetState function open or close Tour

setSteps: Dispatch<React.SetStateAction<StepType[]>>

SetState function to update the Array of steps.

meta: string

Global meta information that could be useful in complex Tour/s situtations

setMeta: Dispatch<React.SetStateAction<string>>

SetState function to update the global meta info.

Warning: Make sure you reset the currentStep value using the setCurrentStep function to ensure the tour will be opened to the correct step after update. Otherwise, in case where a person has already interacted with the tour steps and closed the tours on step 5 for example, they might open to the incorrect step, or similarly if the new set of steps only has 3 steps nothing will open.

withTour

In case you needed there is an enhancer that allows you to have all useTour functionalities through a Higher Order Component.

import { Component } from 'react'
import { withTour } from '@reactour/tour'

class MyComponent extends Component {
  render() {
    return (
      <>
        <button onClick={() => this.props.setIsOpen(true)}>Start Tour</button>
        <div>{/* ... */}</div>
      </>
    )
  }
}

export default withTour(MyCompnent)