Tourist Guide into your React Components
This documentation is for the latest release, which uses npm scoped package
@reactour
. The originalreactour
is now on branchv1
and its documentation can be found here.
npm i -S @reactour/tour
# or
yarn add @reactour/tour
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>
</>
)
}
The Playground is the perfect place to play aroud with all @reactour
Components. Here is an online version.
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.
| key | props |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Badge
| styles
|
| Close
| styles
, onClick
, disabled
|
| Content
| content
,setCurrentStep
,transition
, isHighlightingObserved
,currentStep
,setIsOpen
|
| Navigation
| styles
,setCurrentStep
, steps
, currentStep
, disableDots
, nextButton
, prevButton
, setIsOpen
, hideButtons
, hideDots
, disableAll
, rtl
, Arrow
, |
| Arrow
| styles
, inverted
, disabled
|
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.
Refer to Mask docs and Popover docs for its specific Components
| key | props |
| ---------- | ----------------------------------- |
| badge
| |
| controls
| |
| button
| disabled
|
| arrow
| disabled
|
| dot
| current
, disabled
, showNumber
|
| close
| disabled
|
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 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 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 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 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 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 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.
<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 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 KeyboardHandler = {
keyboardHandler?: (
e: KeyboardEvent,
clickProps?: ClickProps,
status?: {
isEscDisabled?: boolean
isRightDisabled?: boolean
isLeftDisabled?: boolean
}
) => void
}
<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 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 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 PositionType = (
postionsProps: PositionProps,
prev: RectResult
) => 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number]
ContentComponent?: ComponentType<PopoverContentProps>
Completelly custom component to render inside the Popover.
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
}
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 thesetCurrentStep
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)