Using React Shepherd to build a site tour
Tags: react, webdev
Published: 2024-05-07 16:49:25

**Written by [Onuorah Bonaventure](https://blog.logrocket.com/author/onuorahbonaventure/)✏️**

The fastest way to onboard first-time users on your website is by guiding them through some of the features your site has to offer. This phenomenon, also known as a site tour, ultimately shows users how to properly navigate through the site without facing any issues. 

In this tutorial, I will show you how to use [React Shepherd](https://www.npmjs.com/package/react-shepherd) to easily implement a site tour on your website. React Shepherd is a lightweight wrapper library for [Shepherd.js](https://shepherdjs.dev/) that allows us to create a walkthrough of a website — either parts of the site or the whole application — by using dialogs that represent steps that you create. At the end of this article, we will have a functioning site tour [as shown in this video](https://www.youtube.com/watch?v=1uW4ZHdCz3M). 

You can also [check out the project code on GitHub](https://github.com/bonarhyme/site-tour-with-react-shepherd/tree/main). Let’s get started!

## What is React Shepherd?

[React Shepherd](https://www.npmjs.com/package/react-shepherd) is an open source library that wraps around another open source library known as [Shepherd.js](https://shepherdjs.dev/). Its purpose is to make creating site tours easy, efficient, and aesthetic. 

Essentially, React Shepherd provides two convenient ways to access methods and set objects for the tour without complexities: React Hooks and React Context. However, we will be focusing more on how to use React Context and the associated `Provider` component, which is the more convenient option.

## Advantages of React Shepherd

The React Shepherd library comes with a number of advantages that make it a great tool to use for implementing a very powerful site tour in a React project:

1.  It’s highly customizable
2.  It has global state management built with Context
3.  It’s lightweight
4.  It’s highly accessible, providing actions with the user’s keyboard

We’ll see these advantages in action as we go through the step-by-step tutorial below.

## How to use React Shepherd

The React Shepherd library basically accepts two props — `steps` and `tourOptions`. Although only `steps` is required, we should pass both props for the library work properly. 

Generally, the `steps` prop expects an array of objects with the `ShepherdOptionsWithType` interface (or structure or type). Meanwhile, the `tourOptions` prop expects an object with the `TourOptions` interface. 

The library expects that we pass these props to the provided `ShepherdTour` provider or to the `useShepherdTour` Hook, as shown in the sample below:

```javascript
// With ShepherdTour 
  <ShepherdTour steps={newSteps} tourOptions={tourOptions}>
    {children}
  </ShepherdTour>

// With hook
  const tour = useShepherdTour({ tourOptions, steps: newSteps });
```

To start the tour, we can use the `tour.start()` method. We also have other methods available in the `Tour` class to perform other actions, such as stopping the tour.

## The `ShepherdOptionsWithType`, `TourOptions`, and `Tour` interfaces

Let’s explore React Shepherd’s interfaces. As mentioned previously, the required `steps` prop expects an array of objects with the `ShepherdOptionsWithType` interface, so let’s begin there.

### `ShepherdOptionsWithType`

The `ShepherdOptionsWithType` interface typically helps us to create an object that contains the following 15 keys:

*   `attachTo`
*   `arrow`
*   `beforeShowPromise`
*   `buttons`
*   `canClickTarget`
*   `classes`
*   `highlightClass`
*   `id`
*   `scrollTo`
*   `text`
*   `title`
*   `when`
*   `scrollToHandler`
*   `showOn`
*   `cancelIcon`

We can combine these keys into array of objects and pass them to the `steps` prop. Let’s explore each one in more detail. 

The `attachTo` key is used to bind a dialog (or step) to an element. When it's not provided, the dialog remains at the center of the screen. It expects an object with `element` and `on` keys, and can be used like this:

```javascript
attachTo: { 'a html element selector', on: 'bottom'  }
```

The `on` key can accept a lot more options, like:

```plaintext
'top'|'top-start'|'top-end'
'bottom'|'bottom-start'|'bottom-end'
'right'|'right-start'|'right-end'
'left'|'left-start'|'left-end'
```

The `arrow` key accepts a boolean value and is used to show a tooltip on the dialog or to hide it. By default, the tooltip will be shown. 

The `beforeShowPromise` key accepts a promise and is used to run a particular piece of code before other parts of the step will be shown. It can be used as like this:

```typescript
        beforeShowPromise: function () {
          return new Promise(function (resolve: any) {
          // This can be any code
            setTimeout(function () {
              router.push("/about");
              resolve();
            }, 200);
          //
          });
        },
```

The `buttons` key is used to add buttons to the modal. It accepts an array of objects with the following keys:

*   `action` is a function that exposes `this`, which allows us to call methods available in the tour
*   `classes` allow us to style the button with `CSS`
*   `text` allows us to pass the visible text of the button
*   `type` allows us to define how we expect the button to behave. `type` can be any of the following: `'back' | 'cancel' | 'next'`

A `buttons` array can be defined as thus:

```typescript
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
```

You can even omit the type and define an `action` like this:

```typescript
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Restart",
            action() {
              this.cancel();
              router.push("/dashboard");
              this.start();
            },
          },
          {
            classes: "shepherd-button-primary",
            text: "Done",
            type: "cancel",
          },
        ],
```

The `canClickTarget` key is a boolean value. When set to `false`, it ensures that the targeted element where the dialog will be shown will not have a pointer event, meaning that it cannot be clicked at the time of the tour. 

This key is most useful when certain components or elements contain links or sensitive parts of the app and you do not want users to mistakenly click on them during the tour. 

The `classes` key accepts a string of classes separated by an empty space. It is used to style any part of the dialog. 

The `highlightClass` key can be used to style the element we have attached the dialog (or step) to. 

The `id` key is used internally to keep track of steps. Hence, it should be unique for all the steps. 

The `scrollTo` key is used to make the dialog scroll to the attached element or not. 

The `text` key accepts an array of strings and can used to pass paragraphs to the dialog. You would write the array of strings like so:

```plaintext
['first para', 'second para', 'third para']
```

The `title` key, as the name suggests, is used to set the heading for the dialog. 

The `when` key is a handy option that can be used to run functions when a dialog is shown or hidden. It basically accepts two keys: `show` and `hide`. You can use it like so:

```typescript
    when: {
      show: () => {
        document.getElementById("nav-menu")?.click();
      },
      hide: () => {
        console.log('Run some code here');
      }
    }
```

The `scrollToHandler` key accepts a function with a parameter that represents the element attached to the step. It can be used to define a custom scroll or other DOM method. 

The `showOn` key accepts a function that can be used to run a piece of code that generally should return a boolean value. When it return a `false` value, then the step is not shown. It can be used like this:

```typescript
    showOn?: (() => {
 if(someValue === anotherValue) {
    return true
    }
  });
```

And finally, the `cancelIcon` key accepts an object comprised of `enabled` and `label`, which expect a `string` and a `boolean` value, respectively. This key is used to show or hide the icon. 

We can combine these keys to form steps for our tour like this:

```typescript
const steps = [{
        id: "tenth",
        title: "View most recent blog posts",
        text: ["View most recent blog posts at a glance"],
        attachTo: { element: "#blogs", on: "top" },
        scrollTo: true,
        arrow: true,
        classes: '.custom-design'
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
      {
        id: "eleventh",
        title: "Navigate to any page from here",
        text: ["Navigate to the appropriate page by clicking on any"],
        attachTo: { element: "#sidemenu-about", on: "top" },
        scrollTo: true,
        canClickTarget: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Restart",
            action() {
              this.cancel();
              router.push("/dashboard");
              this.start();
            },
          },
          {
            classes: "shepherd-button-primary",
            text: "Done",
            type: "cancel",
          },
        ],
        when: {
          show: () => {
            document.getElementById("sidemenu-about")?.click();
            localStorage.setItem("demo-done", JSON.stringify(true));
          },
          hide: () => {},
        },
        beforeShowPromise: function () {
          return new Promise(function (resolve: any) {
            setTimeout(function () {
              router.push("/about");
              resolve();
            }, 200);
          });
        },
      },
    ];
```

The simple demo above would result in two tour steps, internally labeled with `id` keys of `tenth` and `eleventh`. See if you can determine how each step would appear and behave based on the configurations above. Then, let’s move on to discussing the next interface.

### `tourOptions`

The `tourOptions` interface allows us to add more functionality to the whole tour. For instance, we can use `confirmCancel` to add a `window.confirm` modal when closing the modal by clicking the `x` icon which we can equally show or hide using the `defaultStepOptions` option. 

Some of the options for this interface are as follows:

1.  `confirmCancel`: Accepts a boolean value and can be used to attach a confirmation before closing the whole tour
2.  `confirmCancelMessage`: Accepts a string and it is used to define the message that will be shown in the `window.confirm` modal
3.  `defaultStepOptions`: Accepts all the default options that we can pass to a step; note that enabling this will apply all the default options to all the steps
4.  `exitOnEsc`: Accepts a boolean value and is used to determine whether the `esc` key should close the tour or not. By default, the value is set to `true`
5.  `keyboardNavigation`: Accepts a boolean value and is used to determine whether the user is allowed to navigate through the tour using the `left` and `right` arrow keyboard keys. By default, the value is set to `true`
6.  `tourName`: Used to attach a name to the tour. It is generally used to generate a `id` for the tour.
7.  `useModalOverlay`: Accepts a boolean value. When it is set to `true`, the whole page will have a dark overlay

These properties can be used like so:

```typescript
const tourOptions = {
  defaultStepOptions: {
    cancelIcon: {
      enabled: false,
    },
  },
  useModalOverlay: true,
  keyboardNavigation: true,
  exitOnEsc: true // And so on...
};
```

### `Tour`

The `Tour` class has bunch of methods we can use to perform specific actions, such as starting and canceling the tour. Some of the methods that exist in this class are:

*   `addStep()`: Accepts an object created with the `ShepherdOptionsWithType` interface and can be used to add a new step to the tour
*   `addSteps()`: Similar to `addStep()`; however, it allows us to to add an array of multiple objects created with the `ShepherdOptionsWithType` interface
*   `back()`: Used go back to a previous tour step
*   `next()`: Used to go to the next tour step
*   `start()`: Used to begin the entire tour

Other important methods to know include `removeStep`, which accepts a name as a parameter, and `isActive()`, which is used to indicate that a particular step is the active one. The complete list of methods can be found in [the Shepherd.js documentation](https://shepherdjs.dev/docs/Tour.html).

## Project setup for our React Shepherd site tour

The next step in this tutorial is to implement the tour. To properly follow along, I have provided a modified version of a React project based on this free [dashboard template](https://demos.wrappixel.com/free-admin-templates/react/monster-react-free/main/#/starter). Download or clone this [starter project](https://github.com/bonarhyme/site-tour-with-react-shepherd/tree/starter) containing the essential code needed to follow along. 

Basically, by cloning or downloading this starter code, your folder structure should look similar to the one I have provided below:

```plaintext
// Folder structure
app
 ┣ assets
 ┃ ┗ images
 ┃ ┃ ┣ bg
 ┃ ┃ ┃ ┣ bg1.jpg
 ┃ ┃ ┃ ┣ bg2.jpg
 ┃ ┃ ┃ ┣ bg3.jpg
 ┃ ┃ ┃ ┗ bg4.jpg
 ┃ ┃ ┗ users
 ┃ ┃ ┃ ┣ user1.jpg
 ┃ ┃ ┃ ┣ user2.jpg
 ┃ ┃ ┃ ┣ user3.jpg
 ┃ ┃ ┃ ┣ user4.jpg
 ┃ ┃ ┃ ┗ user5.jpg
 ┣ components
 ┃ ┣ blog.tsx
 ┃ ┣ blogs.tsx
 ┃ ┣ feeds.tsx
 ┃ ┣ header.tsx
 ┃ ┣ layout.tsx
 ┃ ┣ projects-table.tsx
 ┃ ┣ sales-chart.tsx
 ┃ ┗ sidebar.tsx
 ┣ pages
 ┃ ┣ api
 ┃ ┃ ┗ hello.ts
 ┃ ┣ _app.tsx
 ┃ ┣ _document.tsx
 ┃ ┣ about.tsx
 ┃ ┣ dashboard.tsx
 ┃ ┗ index.tsx
 ┣ public
 ┃ ┣ favicon.ico
 ┃ ┣ next.svg
 ┃ ┗ vercel.svg
 ┣ styles
 ┃ ┣ scss
 ┃ ┃ ┣ layout
 ┃ ┃ ┃ ┣ _container.scss
 ┃ ┃ ┃ ┗ _sidebar.scss
 ┃ ┃ ┣ _variables.scss
 ┃ ┃ ┗ style.scss
 ┃ ┗ globals.css
 ┣ .eslintrc.json
 ┣ .gitignore
 ┣ next.config.mjs
 ┣ package-lock.json
 ┗ package.json
```

Once you have the code, you can `cd` into the root folder of the project and run `npm install` or `yarn install` to install dependencies. After the dependencies have been added, you can start the project by running `npm run dev` or `yarn dev`, since this was actually set up using Next.js.

## Adding steps to the React Shepherd site tour

In this section, we will set up each step of the site tour. First, let’s install the React Shepherd library using either of the commands below:

```bash
npm install react-shepherd
//OR
yarn add react-shepherd
```

Next, we will add `tour.tsx` file in the `components` folder alongside components like `blog.tsx`. In the `components/tour.tsx` file, we will import necessary dependencies, including the styles required to make the dialogs look pretty:

```typescript
// components/tour.tsx

import { useRouter } from "next/router";
import { PropsWithChildren, useContext, useEffect, useMemo } from "react";
import {
  ShepherdOptionsWithType,
  ShepherdTour,
  ShepherdTourContext,
} from "react-shepherd";
import { Button } from "reactstrap";
import "shepherd.js/dist/css/shepherd.css";
```

Next, we will create the `tourOption` object like so:

```typescript
// components/tour.tsx

const tourOptions = {
  defaultStepOptions: {
    cancelIcon: {
      enabled: false,
    },
  },
  useModalOverlay: true,
};
```

After that, we will create a `TourInstance` component with the following code below the `tourOptions` object like so:

```typescript
// components/tour.tsx

const TourInstance: React.FC<PropsWithChildren> = ({ children }) => {
  const tour = useContext(ShepherdTourContext);

  useEffect(() => {
    if (typeof window !== "undefined") {
      const demoDone = localStorage.getItem("demo-done")
        ? JSON.parse(localStorage.getItem("demo-done") || "")
        : null;
      if (demoDone) return;
      tour?.start();
    }
  }, [tour]);

  return <>{children}</>;
};
```

In the code above, we extract the tour instance from the `ShepherdTourContext`. Then, in the `useEffect` Hook, we check if the window is defined before using the value of `demo-done` in the `localStorage`. This will prevent the tour from restarting when we’re done with the tour’s steps. 

After that, we run the `tour.start` function. We also return the passed children into the DOM. 

Next, we will create another component where we will pass the `steps` and the `tourOptions` to the `ShepherdTour` consumer. Our code will look similar to this:

```typescript
// components/tour.tsx

const Tour: React.FC<PropsWithChildren> = ({ children }) => {
  const router = useRouter();
  const newSteps: ShepherdOptionsWithType[] = []

  return (
    <ShepherdTour steps={newSteps} tourOptions={tourOptions}>
      <TourInstance>{children}</TourInstance>
    </ShepherdTour>
  );
};
export default Tour;
```

The next process is to define the steps. In total, we will have eleven steps, which will have different configurations:

```typescript
// components/tour.tsx

  const newSteps: ShepherdOptionsWithType[] = useMemo(() => {
    return [
      {
        id: "first",
        title: "Welcome to App Dashboard",
        text: [
          "Let us take you on a journey to explore the most important features of our site",
        ],
        scrollTo: false,
        arrow: false,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
```

The first step defined above only has six configurations attached. It has been set up this way so that we can place it at the center of the page, since we don’t want it to be attached to any item. The properties `scrollTo` and `arrowTo` were set to `false`, and we added only two buttons — `Exit` and `Next`. 

Here’s the second step:

```typescript
      {
        id: "second",
        title: "App navigation bar",
        text: ["Explore how you can easily navigate through the app"],
        attachTo: { element: "#navigation", on: "bottom" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
```

The second dialog was attached to an element with an `id` of `#navigation`, which can be found in `components/header.tsx`. Notice how the `on` key has a value of `bottom` because we want the dialog to be positioned at the right of the element. 

Also, the `scrollTo` property has now been set to `true` because we expect the dialog to be moved to the `#navigation` element. There are also three buttons now, adding a `Back` button to the `Exit` and `Next` buttons we already set up in the previous step. 

Notice how the class provided by Shepherd.js was attached to the buttons. This will ensure we have very neat default styles when we finally use the code. 

Let’s take a look at the third and fourth tour steps:

```typescript
      {
        id: "third",
        title: "Navigate to the dashboard",
        text: ["Click here to quickly navigate to the dashboard index"],
        attachTo: { element: "#nav-dashboard", on: "bottom" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
      {
        id: "fourth",
        title: "Navigate to about page",
        text: ["Click here to quickly navigate to the about page"],
        attachTo: { element: "#nav-about", on: "bottom" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
```

The third and fourth steps are similar to the second step, except that they have been attached to different elements. These steps have an `id` of `#nav-dashboard` and `#nav-about`, respectively, which can still be found in the `components/header.tsx` file. 

Next, we’ll look at the fifth and sixth steps:

```typescript
      {
        id: "fifth",
        title: "Open extra menu options",
        text: ["Click here to open extra menu options"],
        attachTo: { element: "#nav-menu", on: "right" },
        scrollTo: true,
        canClickTarget: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
        when: {
          show: () => {
            document.getElementById("nav-menu")?.click();
          },
          hide: () => {},
        },
      },
      {
        id: "sixth",
        title: "Open profile options",
        text: ["Click here to open profile options"],
        attachTo: { element: "#nav-profile", on: "left" },
        canClickTarget: true,
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
        when: {
          show: () => {
            document.getElementById("nav-profile")?.click();
          },
          hide: () => {
            console.log("hide step");
          },
        },
      },
```

The fifth and sixth steps are similar to our previous steps. However, we attached them to different elements using their `id` keys — `#nav-menu` and `#nav-profile`, respectively. 

We have also added two extra properties to the steps — `canClickTarget` and `when`. This is so we automatically click on the items being discussed when we are on their respective steps. 

We’ll look at the code for the seventh, eighth, ninth, and tenth steps all together:

```typescript
      {
        id: "seventh",
        title: "Sales overview",
        text: ["Graph that contains visual details about the sales prohgress"],
        attachTo: { element: "#sales-chart", on: "bottom" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
      {
        id: "eighth",
        title: "Quickly find out what's going on",
        text: ["Updates on what is going on..."],
        attachTo: { element: "#feed", on: "left" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
      {
        id: "ninth",
        title: "Overview of projects listings",
        text: ["Summary of projects carried out"],
        attachTo: { element: "#projects-listing", on: "top" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
      {
        id: "tenth",
        title: "View most recent blog posts",
        text: ["View most recent blog posts at a glance"],
        attachTo: { element: "#blogs", on: "top" },
        scrollTo: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Exit",
            type: "cancel",
          },
          {
            classes: "shepherd-button-primary",
            text: "Back",
            type: "back",
          },
          {
            classes: "shepherd-button-primary",
            text: "Next",
            type: "next",
          },
        ],
      },
```

Steps 7–10 are also similar to the ones above, except for the elements they are attached to:

*   `#sales-chart`, which can be found in the `components/sales-chart.tsx` file
*   `#feeds`, which can be found in the `components/feeds.tsx` file
*   `#projects-listing`, which can be found in the `components/projects-listing.tsx` file
*   `#blogs`, which can be found in the `components/blogs.tsx` file

The eleventh step is the most interesting of all:

```typescript
      {
        id: "eleventh",
        title: "Navigate to any page from here",
        text: ["Navigate to the appropriate page by clicking on any"],
        attachTo: { element: "#sidemenu-about", on: "top" },
        scrollTo: true,
        canClickTarget: true,
        buttons: [
          {
            classes: "shepherd-button-secondary",
            text: "Restart",
            action() {
              this.cancel();
              router.push("/dashboard");
              this.start();
            },
          },
          {
            classes: "shepherd-button-primary",
            text: "Done",
            type: "cancel",
          },
        ],
        when: {
          show: () => {
            document.getElementById("sidemenu-about")?.click();
            localStorage.setItem("demo-done", JSON.stringify(true));
          },
          hide: () => {},
        },
        beforeShowPromise: function () {
          return new Promise(function (resolve: any) {
            setTimeout(function () {
              router.push("/about");
              resolve();
            }, 200);
          });
        },
      },
    ];
  }, []);
```

What makes this step so interesting is that we have to route to a different page on the website without actually stopping the tour. We also have to restart the tour right from the other page. Initially, we start off in the `/dashboard` page before finally navigating to the `/about` page. 

This can be handled using a series of steps. First, we use the `show` method inside the `when` property to click on the `#sidemenu-about` on the sidebar. Then, we set the `demo-done` value to `true` inside the `localStorage`. 

Next, we implemented the `beforeShowPromise` function, which we use to ensure that we navigate to the `/about` page before we even show the dialog. We added a custom `Restart` button to the step, which implements an action that we use to cancel the existing tour, navigate back to the dashboard page, and start a new tour.

## Implementing our custom React Shepherd tour

Now that we have defined our steps, we can use our tour in our project. We start by first wrapping the entry to our app — `pages/_app.tsx` — with the the `Tour` component we have created. 

One thing to notice is the dynamic import we have done for the `Tour`, which was done to fix any issues that might arise while using our project with Next.js. Our `pages/_app.tsx` should now look like this:

```typescript
// pages/_app.tsx

import { Layout } from "@/components/layout";
import "@/styles/globals.css";
import "@/styles/scss/style.scss";
import type { AppProps } from "next/app";
import dynamic from "next/dynamic";
import "shepherd.js/dist/css/shepherd.css";

const Tour = dynamic(() => import("../components/tour"), { ssr: false });

export default function App({ Component, pageProps }: AppProps) {
  return (
    <Tour>
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </Tour>
  );
}
```

By now, our app should have a demo that functions as shown in the [YouTube video above](https://www.youtube.com/watch?v=1uW4ZHdCz3M). Our site tour now works with the React Provider–React Context method, but we can augment it further — for example, by adding a **Start** button that allows a user restart the tour at any given time. 

Let’s see how to add this button now. We have to open `components/sidebar.tsx` file, add the `handleStartTour` function, and add the **Start tour** button to the DOM like so:

```typescript
// components/sidebar.tsx

import Link from "next/link";
import { useRouter } from "next/router";
import { useContext } from "react";
import { ShepherdTourContext } from "react-shepherd";
import { Button, Nav, NavItem } from "reactstrap";

const navigation = [
  {
    title: "Dashboard",
    href: "/dashboard",
    icon: "bi bi-speedometer2",
    id: "sidemenu-dashboard",
  },
  {
    title: "About",
    href: "/about",
    icon: "bi bi-people",
    id: "sidemenu-about",
  },
];
export const Sidebar = () => {
  const location = useRouter();

  // Import the context and define the tour
  const tour = useContext(ShepherdTourContext);

  const showMobilemenu = () => {
    if (typeof window !== "undefined") {
      document?.getElementById("sidebarArea")?.classList.toggle("showSidebar");
    }
  };

  const handleStartTour = () => {
    if (typeof window !== "undefined") {
      localStorage.removeItem("demo-done");
      // Restart the tour any time
      tour?.start();
    }
  };

  return (
    <div className="p-3">
      <div className="d-flex align-items-center">
        <Link
          href={"/"}
          className="nav-link border py-2 px-4 bg-dark text-white"
        >
          App LOGO
        </Link>
        <span className="ms-auto d-lg-none">
          <Button
            close
            size="sm"
            className="ms-auto d-lg-none"
            onClick={() => showMobilemenu()}
          ></Button>
        </span>
      </div>
      <div className="pt-4 mt-2">
        <Nav vertical className="sidebarNav">
          {navigation.map((navi, index) => (
            <NavItem key={index} className="sidenav-bg" id={navi.id}>
              <Link
                href={navi.href}
                className={
                  location.pathname === navi.href
                    ? "text-primary nav-link py-3"
                    : "nav-link text-secondary py-3"
                }
              >
                <i className={navi.icon}></i>
                <span className="ms-3 d-inline-block">{navi.title}</span>
              </Link>
            </NavItem>
          ))}
          <Button color="primary" onClick={handleStartTour} className="mt-4">
            Start tour
          </Button>
        </Nav>
      </div>
    </div>
  );
};
```

As I mentioned earlier, React Shepherd provides both React Hooks and React Context with its Provider component for accessing methods and setting objects for the tour. We focused on using the Context method in this tutorial. 

However, let’s still take a look at using the Hook provided by the React Shepherd library. This is totally optional, but may be of help to you as you work with React Shepherd. 

This implementation is quite simple — we just have to open the `components/tour.tsx` file and add the following code to it:

```typescript
// components/tour.tsx  

const tour = useShepherdTour({ tourOptions, steps: newSteps });

  return (
    <>
      <Button
        onClick={tour.start}
        style={{
          position: "fixed",
          bottom: "50vh",
          left: "24px",
          minWidth: 200,
        }}
      >
        Start tour with hook
      </Button>
      {children}
    </>
  );
  // return (
  //   <ShepherdTour steps={newSteps} tourOptions={tourOptions}>
  //     <TourInstance>{children}</TourInstance>
  //   </ShepherdTour>
  // );
```

## Conclusion

React Shepherd is a really great tool for site creating site tours in a React app. In this tutorial, we have used it to create a site of a dashboard site created with React. 

There are many other libraries available for creating site tours, including [React Tour and React Joyride](https://blog.logrocket.com/complete-guide-to-build-product-tours-on-your-react-apps/), [vue-tour for Vue projects](https://blog.logrocket.com/crafting-product-tours-in-vue-js/), [the Driver.js library](https://blog.logrocket.com/using-driver-js-guide-user-focus/), and more. Among these, React Shepherd stands out due to the elegant UI it provides along with its out-of-the-box, easy-to-use React Context implementation. 

The React Tour package makes it easy to actually make any part of the app “tour-able,” meaning that we can add tour steps to any part of the app from a single point of truth. This often requires extra steps in other packages. 

To review and play around with the code we implemented above, [check out this GitHub repository](https://github.com/bonarhyme/site-tour-with-react-shepherd/tree/main). If you have any questions, you’re welcome to comment below.