# Web React Tour Library
## 1. React JoyRide
Summary:
- Accesibility (esc to exit, using tab for navigation)
- Beacons / spotlight component (built in & custom)
- Inline styling
- Support: custom, controlled, carousel, modal, scroll
- Event built ini: Click or Hover
- Target with CSS selector
- bundle: 98.9 kB Minified, 30.3 kB Gzip

- 13 open issues
- 4 month ago of last commit
- Used by 2.6k
- Weekly download: 138K
- codesandbox demo: https://codesandbox.io/s/5x6m6o98vp
- Get started:
```jsx=
import Joyride from 'react-joyride';
export class App extends React.Component {
state = {
steps: [
{
target: '.my-first-step',
content: 'This is my awesome feature!',
},
{
target: '.my-other-step',
content: 'This another awesome feature!',
},
...
]
};
render () {
const { steps } = this.state;
return (
<div className="app">
<Joyride
steps={steps}
...
/>
...
</div>
);
}
}
```
## 2. React Tour Js
Pro:
-
- Popover, Mask, Provider
- Accesibility (esc to exit, using tab/arrow for navigation)
- Beacon / spotlight need to be created our own self
- Target with CSS selector
- Emotion styling
- 38.7 kB Minified 12.1 kB Gzip

- peer dependency: emotion
- 9 days of last commit
- weekly download 42.9K
- codesandbox demo: https://codesandbox.io/s/reactour-tour-demo-using-react-router-dom-kujql
- Get started
`wrap our app/page/component first`
```jsx=
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 use useTour anywhere`
```jsx=
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>
</>
)
}
```
## 3. Intro Js React
- Steps, Hints(beacon)
- bundle size(intro.js): 52.4 kB minified. 16.8 kB Gzip

- bundle size(react wrapper): 12.4 kB minified, 2.9 kB Gzip
- around 65 kB in total minified
- css class styling
- used by: 2.9 k (intro.js)
- used by: 324 (intro react wrapper)
- weekly download: 61k
- 5 month last commit
- codesandbox demo https://codesandbox.io/embed/o2A4gwXE3?hidenavigation=1
- Get started:
```jsx=
import React, { Component } from "react";
import { render } from "react-dom";
import { Steps, Hints } from "intro.js-react";
import "intro.js/introjs.css";
import "./index.css";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
stepsEnabled: true,
initialStep: 0,
steps: [
{
element: ".hello",
intro: "Hello step"
},
{
element: ".world",
intro: "World step"
}
],
hintsEnabled: true,
hints: [
{
element: ".hello",
hint: "Hello hint",
hintPosition: "middle-right"
}
]
};
}
render() {
const {
stepsEnabled,
steps,
initialStep,
hintsEnabled,
hints
} = this.state;
return (
<div>
<Steps
enabled={stepsEnabled}
steps={steps}
initialStep={initialStep}
onExit={this.onExit}
/>
<Hints enabled={hintsEnabled} hints={hints} />
<div className="controls">
<div>
<button onClick={this.toggleSteps}>Toggle Steps</button>
<button onClick={this.addStep}>Add Step</button>
</div>
<div>
<button onClick={this.toggleHints}>Toggle Hints</button>
<button onClick={this.addHint}>Add Hint</button>
</div>
</div>
<h1 className="hello">Hello,</h1>
<hr />
<h1 className="world">World!</h1>
<hr />
<h1 className="alive">It's alive!</h1>
</div>
);
}
onExit = () => {
this.setState(() => ({ stepsEnabled: false }));
};
toggleSteps = () => {
this.setState(prevState => ({ stepsEnabled: !prevState.stepsEnabled }));
};
addStep = () => {
const newStep = {
element: ".alive",
intro: "Alive step"
};
this.setState(prevState => ({ steps: [...prevState.steps, newStep] }));
};
toggleHints = () => {
this.setState(prevState => ({ hintsEnabled: !prevState.hintsEnabled }));
};
addHint = () => {
const newHint = {
element: ".alive",
hint: "Alive hint",
hintPosition: "middle-right"
};
this.setState(prevState => ({ hints: [...prevState.hints, newHint] }));
};
}
render(<App />, document.getElementById("root"));
```
# React Native Tour Library
## 1. React Native Copilot
- weekly download 2.6K
- Tooltip, Animation(need rn-svg), customizable mask & step
- Support i18n
- Support Highlight: Image, Text/Icon
- Event: start, stop, stepChange
- 16 month last commit
- 65 issues
- used by 162
- 52.3 kB unpacked
- Get stared:
```jsx=
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, Image, View, TouchableOpacity, Switch } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { copilot, walkthroughable, CopilotStep } from 'react-native-copilot';
const WalkthroughableText = walkthroughable(Text);
const WalkthroughableImage = walkthroughable(Image);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
paddingTop: 40,
},
title: {
fontSize: 24,
textAlign: 'center',
},
profilePhoto: {
width: 140,
height: 140,
borderRadius: 70,
marginVertical: 20,
},
middleView: {
flex: 1,
alignItems: 'center',
},
button: {
backgroundColor: '#2980b9',
paddingVertical: 10,
paddingHorizontal: 15,
},
buttonText: {
color: 'white',
fontSize: 16,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
},
tabItem: {
flex: 1,
textAlign: 'center',
},
activeSwitchContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 20,
alignItems: 'center',
paddingHorizontal: 40,
},
});
class App extends Component {
static propTypes = {
start: PropTypes.func.isRequired,
copilotEvents: PropTypes.shape({
on: PropTypes.func.isRequired,
}).isRequired,
};
state = {
secondStepActive: true,
};
componentDidMount() {
this.props.copilotEvents.on('stepChange', this.handleStepChange);
this.props.start();
}
handleStepChange = (step) => {
console.log(`Current step is: ${step.name}`);
}
render() {
return (
<View style={styles.container}>
<CopilotStep text="Hey! This is the first step of the tour!" order={1} name="openApp">
<WalkthroughableText style={styles.title}>
{'Welcome to the demo of\n"React Native Copilot"'}
</WalkthroughableText>
</CopilotStep>
<View style={styles.middleView}>
<CopilotStep active={this.state.secondStepActive} text="Here goes your profile picture!" order={2} name="secondText">
<WalkthroughableImage
source={{ uri: 'https://pbs.twimg.com/profile_images/527584017189982208/l3wwN-l-_400x400.jpeg' }}
style={styles.profilePhoto}
/>
</CopilotStep>
<View style={styles.activeSwitchContainer}>
<Text>Profile photo step activated?</Text>
<View style={{ flexGrow: 1 }} />
<Switch
onValueChange={secondStepActive => this.setState({ secondStepActive })}
value={this.state.secondStepActive}
/>
</View>
<TouchableOpacity style={styles.button} onPress={() => this.props.start()}>
<Text style={styles.buttonText}>START THE TUTORIAL!</Text>
</TouchableOpacity>
</View>
<View style={styles.row}>
<CopilotStep text="Here is an item in the corner of the screen." order={3} name="thirdText">
<WalkthroughableText style={styles.tabItem}>
<Ionicons name="ios-contact" size={40} color="#888" />
</WalkthroughableText>
</CopilotStep>
<Ionicons style={styles.tabItem} name="ios-game-controller-b" size={40} color="#888" />
<Ionicons style={styles.tabItem} name="ios-globe" size={40} color="#888" />
<Ionicons style={styles.tabItem} name="ios-navigate-outline" size={40} color="#888" />
<Ionicons style={styles.tabItem} name="ios-rainy" size={40} color="#888" />
</View>
</View>
);
}
}
export default copilot({
animated: true, // Can be true or false
overlay: 'svg', // Can be either view or svg
})(App);
```
## 2. react-native-onboarding-swiper
- Like a caraouse
- Simple, Straighforward & flexible
- Accepted content text, image, button
- a week last commit
- used by 2.2k
- 3.9k weekly download
- 20 issues
- 32.7 kB unpacked
- Get started:
```jsx=
import { Image } from 'react-native';
import React from 'react';
import Onboarding from 'react-native-onboarding-swiper';
const Simple = () => (
<Onboarding
onDone={() => console.log('done')}
pages={[
{
backgroundColor: '#fff',
image: <Image source={require('./images/circle.png')} />,
title: 'Onboarding',
subtitle: 'Done with React Native Onboarding Swiper',
},
{
backgroundColor: '#fe6e58',
image: <Image source={require('./images/square.png')} />,
title: 'The Title',
subtitle: 'This is the subtitle that sumplements the title.',
},
{
backgroundColor: '#999',
image: <Image source={require('./images/triangle.png')} />,
title: 'Triangle',
subtitle: "Beautiful, isn't it?",
},
]}
/>
);
export default Simple;
```
## 3. rn-tourguide
- Support i18n
- Provider, mask, tooltip
- Event: start, stop, stepChange
- 1.7k weekly download
- used by 37
- 14 days last commit
- need rn-svg
- 30 issues
- 62.7 kB unpacked
- Get started:
```jsx=
import {
TourGuideProvider, // Main provider
TourGuideZone, // Main wrapper of highlight component
TourGuideZoneByPosition, // Component to use mask on overlay (ie, position absolute)
useTourGuideController, // hook to start, etc.
} from 'rn-tourguide'
// Add <TourGuideProvider/> at the root of you app!
function App() {
return (
<TourGuideProvider {...{ borderRadius: 16 }}>
<AppContent />
</TourGuideProvider>
)
}
const AppContent = () => {
const iconProps = { size: 40, color: '#888' }
// Use Hooks to control!
const {
canStart, // a boolean indicate if you can start tour guide
start, // a function to start the tourguide
stop, // a function to stopping it
eventEmitter, // an object for listening some events
} = useTourGuideController()
// Can start at mount 🎉
// you need to wait until everything is registered 😁
React.useEffect(() => {
if (canStart) {
// 👈 test if you can start otherwise nothing will happen
start()
}
}, [canStart]) // 👈 don't miss it!
const handleOnStart = () => console.log('start')
const handleOnStop = () => console.log('stop')
const handleOnStepChange = () => console.log(`stepChange`)
React.useEffect(() => {
eventEmitter.on('start', handleOnStart)
eventEmitter.on('stop', handleOnStop)
eventEmitter.on('stepChange', handleOnStepChange)
return () => {
eventEmitter.off('start', handleOnStart)
eventEmitter.off('stop', handleOnStop)
eventEmitter.off('stepChange', handleOnStepChange)
}
}, [])
return (
<View style={styles.container}>
{/*
Use TourGuideZone only to wrap your component
*/}
<TourGuideZone
zone={2}
text={'A react-native-copilot remastered! 🎉'}
borderRadius={16}
>
<Text style={styles.title}>
{'Welcome to the demo of\n"rn-tourguide"'}
</Text>
</TourGuideZone>
<View style={styles.middleView}>
<TouchableOpacity style={styles.button} onPress={() => start()}>
<Text style={styles.buttonText}>START THE TUTORIAL!</Text>
</TouchableOpacity>
<TourGuideZone zone={3} shape={'rectangle_and_keep'}>
<TouchableOpacity style={styles.button} onPress={() => start(4)}>
<Text style={styles.buttonText}>Step 4</Text>
</TouchableOpacity>
</TourGuideZone>
<TouchableOpacity style={styles.button} onPress={() => start(2)}>
<Text style={styles.buttonText}>Step 2</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={stop}>
<Text style={styles.buttonText}>Stop</Text>
</TouchableOpacity>
<TourGuideZone
zone={1}
shape='circle'
text={'With animated SVG morphing with awesome flubber 🍮💯'}
>
<Image source={{ uri }} style={styles.profilePhoto} />
</TourGuideZone>
</View>
<View style={styles.row}>
<TourGuideZone zone={4} shape={'circle'}>
<Ionicons name='ios-contact' {...iconProps} />
</TourGuideZone>
<Ionicons name='ios-chatbubbles' {...iconProps} />
<Ionicons name='ios-globe' {...iconProps} />
<TourGuideZone zone={5}>
<Ionicons name='ios-navigate' {...iconProps} />
</TourGuideZone>
<TourGuideZone zone={6} shape={'circle'}>
<Ionicons name='ios-rainy' {...iconProps} />
</TourGuideZone>
<TourGuideZoneByPosition
zone={7}
shape={'circle'}
isTourGuide
bottom={30}
left={35}
width={300}
height={300}
/>
</View>
</View>
)
}
```