# 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 ![](https://i.imgur.com/ODlTnfZ.png) - 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 ![](https://i.imgur.com/BGhyEGO.png) - 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 ![](https://i.imgur.com/H35B02q.png) - 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> ) } ```