@reactour/tour
v3.7.0
Published
<p align="center"> <a href="reactour.js.org"> <img alt="Reactour" title="Reactour" src="https://raw.githubusercontent.com/elrumordelaluz/reactour/main/logo.svg" width="250"></a> </p> <p align="center" style="margin-top: 40px;margin-bottom: 40px;"> <st
Downloads
247,105
Maintainers
Readme
Documentation
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.
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
- Using React Router
- Using React Router with automatic route switching
- Using React Modal
- Using Semantic UI Modal
- Using React Bootstrap Modal
- Tour with data fetching
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.
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
| 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.
Style keys and props available
Refer to Mask docs and Popover docs for its specific Components
Tour 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)