How to add an interactive tutorial to a website with React
Intro, or for what reason you should bother creating interactive tutorials
Hello again. In this article, you will learn how to quickly and easily create an interactive tutorial for your React tool. And the first question I would ask is: is it even necessary?
And indeed, if the functionality of the tool is as simple and intuitive as possible, then there is no need to bother with a tutorial. Ideally, this is what you need to strive for, so that the user starts using your tool right away.
But as it happens in real life, not all tools are easy-to-understand, and many have large and complex functionality. Take the following web tools, for example:
- Topvisor
- Google Analytics
You can't just jump into them and use them. First, you have to learn how to use them. In my case, the tool turned out to be not very intuitive. Therefore, we will make an embedded tutorial on the site.
Creating new events for launching a tutorial
We already have a button to call up a chain of hints.
Now we need to set it up so that this chain works. We will do this by creating our own event.
In the Header.js file, replace
export default function Header() {
 const header_buttons = document.getElementById('meta-header')
 const btns = []
 for ( const btn of header_buttons.children){
  const ref = btn.firstElementChild.getAttribute('href')
  if (btn.dataset.type == 'inner-link'){
   btns.push()
  }
  else if (btn.dataset.type == 'tutorial' ){
   btns.push()
  }
export default function Header() {
 const header_buttons = document.getElementById('meta-header')
 const btns = []
 for ( const btn of header_buttons.children){
  const ref = btn.firstElementChild.getAttribute('href')
  if (btn.dataset.type == 'inner-link'){
   btns.push()
  }
  else if (btn.dataset.type == 'tutorial' ){
   btns.push()
  }
We added an ID to the button + our own click event.
Creating a new component, Tutorial.js
Now let's create a new component. In the src/components directory, create a file Tutorial.js. Then add the following code there:
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Popover from '@mui/material/Popover';
export default function Hint({children, anchor}) {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const [counter, setCounter] = React.useState(1);
  const [message, setMessage] = React.useState('');
  const clearAllHints = () => {
    const hints = document.querySelectorAll('.tutorial_hint')
    hints.forEach( (hint_target) => {
      hint_target.style = ''
      if (hint_target.classList.contains('to_be_hidden')){
        hint_target.classList.add('hidden')
        hint_target.classList.remove('to_be_hidden')
      }
    })
  }
  const displayNextHint = () => {
    const hints = document.querySelectorAll('.tutorial_hint')
    hints.forEach( (hint_target) => {
      if (parseInt(hint_target.dataset.queue) == counter){
        hint_target.style = 'background-color: orange'
        if (hint_target.classList.contains('hidden')){
          hint_target.classList.remove('hidden')
          hint_target.classList.add('to_be_hidden')
        }
        var hint_msg = document.getElementById(`tutorial_hint_${hint_target.dataset.queue}`)
        setMessage(hint_msg.innerText)
        setAnchorEl(hint_target)
      }
    })
    setCounter(counter + 1)
  }
  const handleClick = (event) => {
    clearAllHints()
    displayNextHint()
  };
  let tutorial_button = document.getElementById('onTutorial')
  tutorial_button.addEventListener('onTutorial', handleClick, {once: true})
  const handleClose = () => {
    const hints = document.querySelectorAll('.tutorial_hint')
    if (hints.length < counter){
      setMessage('')
      setCounter(1)
      setAnchorEl(null);
      clearAllHints()
    }
    else{
      clearAllHints()
      displayNextHint()
    }
  };
  const open = Boolean(anchorEl);
  const id = open ? 'simple-popover' : undefined;
  return (
   Â
    {message}
   Â
  );
}
const popover = document.getElementById('popover-tutorial');
const root = createRoot(popover);
root.render( );
The essence of this code boils down to searching for marked elements with the tutorial_hint class and rendering a hint next to this element. The function that is worth paying attention to is displayNextHint. Having found all the marked elements, it finds the element associated with it and takes the hint text from there. Then it selects the hint element and shows the hint itself.
In order for the component to be able to render, we will add another element to app.html, right before the app_settings element.
And we will connect the new component to index.js
Marking elements for hints
All that remains is to mark the necessary elements for hints.
export default function AppActions(){
  const [isModal, setModal] = React.useState(false);
  const req = {'setModal': setModal}
  return (
   Â
      Optional: Then you can save all of your configurations
      {setModal(true); waitTillModalIsUp(SaveRequest, req)}} className='w-fit tutorial_hint' data-queue="4">
      {setModal(false)}}
        >
       Â
       Â
     Â
      Step 3: And now you can gather all informatino from SERP results
      {StartParsingRequest(req)}} className='w-fit tutorial_hint'>
   Â
  )
}
export default function AppQueries(){
…
  return (
   Â
     Â
       Â
          {engine_list}
       Â
     Â
      Step 2: Choose an required engine to parse. And then type your query
      handleClick(ev,engine_list)} className='w-fit tutorial_hint' data-queue="2">
   Â
  )
}
export default function AppSettings(){
...
return (
   Â
     Â
        Step 1: Check at least one checkbox, to choose what to save
       Â
         Â
       Â
     Â
      {/* Icon button is gonna be changed */}
     Â
        { isSettings   ?
                : }
     Â
   Â
  )
}
export default function AppUtils(){
...
  return (
   Â
     Â
        Here you can find the most popular and ready-to-use presets
       Â
        Here you will find your own presets
       Â
        Here you will see the actual proccess of gathering information
       Â
        It is a link to a results of parsing proccess
       Â
     Â
...
}
Conclusions or how it is supposed to look
As a result, it will all look something like this:
If you missed everything and want the project as-is, download it here.
In the next article, we will add user logging and authentication.
0