3 horizontal lines, burger
3 horizontal lines, burger
3 horizontal lines, burger
3 horizontal lines, burger

3 horizontal lines, burger
Remove all
LOADING ...

Content



    How to add an interactive tutorial to a website with React

    Clock
    29.08.2024
    /
    Clock
    02.10.2025
    /
    Clock
    4 minutes
    An eye
    295
    Hearts
    0
    Connected dots
    0
    Connected dots
    0
    Connected dots
    0

    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:
    1. Topvisor
    2. 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.

    Before

    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(<Button color='primary'><a href={ref}>{btn.innerText}</a><LinkIcon className='mb-2' fontSize='small' /></Button>) } else if (btn.dataset.type == 'tutorial' ){ btns.push(<Button color='primary'><a href={ref}>{btn.innerText}</a><QuestionMarkIcon className='mb-2' fontSize='small'/></Button>) }

    After

    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(<Button color='primary'><a href={ref}>{btn.innerText}</a><LinkIcon className='mb-2' fontSize='small' /></Button>) } else if (btn.dataset.type == 'tutorial' ){ btns.push(<Button id='onTutorial' onClick={(e)=>{ const onTutorialEvent = new Event('onTutorial') e.currentTarget.dispatchEvent(onTutorialEvent) }} color='primary'><a href={ref}>{btn.innerText}</a><QuestionMarkIcon className='mb-2' fontSize='small'/></Button>) }
    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 ( <Popover id={id} open={open} anchorEl={anchorEl} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', }} transformOrigin={{ vertical: 'top', horizontal: 'left', }} > {message} </Popover> ); } const popover = document.getElementById('popover-tutorial'); const root = createRoot(popover); root.render(<Hint></Hint>);
    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.
    <div id="popover-tutorial"></div>
    And we will connect the new component to index.js

    Mark the required items for hints

    There is only one thing left to do. Mark the required items for hints.

    File AppActions.js

    export default function AppActions(){ const [isModal, setModal] = React.useState(false); const req = {'setModal': setModal} return ( <Box className="flex gap-1"> <div id="tutorial_hint_4" className="hidden"> Optional: Then you can save all of your configurations</div> <IconButton id='onSaveRequest' onClick={()=>{setModal(true); waitTillModalIsUp(SaveRequest, req)}} className='w-fit tutorial_hint' data-queue="4"><SaveIcon className=" border-2 rounded-md"/></IconButton> <Modal open={isModal} onClose={()=>{setModal(false)}} > <Box id='utils-modal' className="absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-2/4 shadow-md p-4 bg-white"> </Box> </Modal> <div id="tutorial_hint_3" className="hidden">Step 3: And now you can gather all informatino from SERP results</div> <IconButton id='onStartParsing' data-queue="3" onClick={()=>{StartParsingRequest(req)}} className='w-fit tutorial_hint'><NotStartedIcon className=" border-2 rounded-md"/></IconButton> </Box> ) }

    File AppQueries.js

    export default function AppQueries(){ … return ( <div> <Popover id={id} open={open} anchorEl={anchorEngine} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', }} > <List sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}> {engine_list} </List> </Popover> <div id="tutorial_hint_2" className="hidden">Step 2: Choose an required engine to parse. And then type your query</div> <IconButton onClick={(ev)=>handleClick(ev,engine_list)} className='w-fit tutorial_hint' data-queue="2"><AddBoxIcon/></IconButton> </div> ) }

    File AppSettings.js

    export default function AppSettings(){ ... return ( <div> <Paper elevation={2} className="z-10"> <div id="tutorial_hint_1" className="hidden" >Step 1: Check at least one checkbox, to choose what to save</div> <div id="settings_content" className='hidden'> <SettingsContent /> </div> </Paper> {/* Icon button is gonna be changed */} <IconButton onClick={ToggleSettings} className='w-fit tutorial_hint' data-queue="1"> { isSettings ? <CloseIcon className=" border-2 rounded-md"/> : <SettingsApplicationsIcon className=" border-2 rounded-md"/>} </IconButton> </div> ) }

    File AppUtils.js

    export default function AppUtils(){ ... return ( <Box className="flex flex-col"> <Box className="flex gap-1"> <div id="tutorial_hint_5" className="hidden">Here you can find the most popular and ready-to-use presets</div> <IconButton onClick={ToggleTrending(true)} className='w-fit border tutorial_hint' data-queue="5"><TrendingUpIcon className=" border-2 rounded-md"/></IconButton> <div id="tutorial_hint_6" className="hidden">Here you will find your own presets</div> <IconButton onClick={ToggleOwnSaves(true)} className='w-fit tutorial_hint' data-queue="6"><SavedSearchIcon className=" border-2 rounded-md"/></IconButton> <div id="tutorial_hint_7" className="hidden">Here you will see the actual proccess of gathering information</div> <div id="console-button" className='hidden tutorial_hint' data-queue="7"><IconButton onClick={ToggleConsole(true)} className='w-fit'><FeaturedPlayListIcon className=" border-2 rounded-md"/></IconButton></div> <div id="tutorial_hint_8" className="hidden">It is a link to a results of parsing proccess</div> <div id="results-button" className='hidden tutorial_hint' data-queue="8"><IconButton id="results-button-ref" href='#' onClick={()=>{}} className='w-fit'><DownloadIcon color='warning' className=" border-2 rounded-md"></DownloadIcon></IconButton></div> </Box> ... }

    Conclusions or how it is supposed to look

    As a result, it will all look something like this:
    You can also poke around on the site yourself.
    If you missed everything above and just want to get the project in its current state, you can download it here. In the next article, we will create the ability for users to log in and register on our site.

    Do not forget to share, like and leave a comment :)

    Comments

    (0)

    captcha
    Send
    LOADING ...
    It's empty now. Be the first (o゚v゚)ノ

    Other

    Similar articles


    Series of articles about creating and promoting SearchResultParser | Tim the webmaster

    Clock
    16.07.2024
    /
    Clock
    05.10.2025
    An eye
    448
    Hearts
    0
    Connected dots
    0
    Connected dots
    0
    Connected dots
    0
    This is an article that is going to introduce you to my new project/webtool, SearchResultParser. Also, from this article, you can navigate to any interesting article for you. See them …

    Developing frontend part of a website with React on Django | SearchResultParser p. 2

    Clock
    16.08.2024
    /
    Clock
    02.10.2025
    An eye
    607
    Hearts
    0
    Connected dots
    0
    Connected dots
    0
    Connected dots
    0
    I show and tell how to develop a frontend for a site on React with a backend on django. I use MaterialUI and TailwindCSS, with source code and comments.

    How to implement localization and translation for django website (python, js, templates and models) p. 5

    Clock
    06.02.2025
    /
    Clock
    02.10.2025
    An eye
    2630
    Hearts
    0
    Connected dots
    0
    Connected dots
    0
    Connected dots
    0
    In this article, I will show how you can add localization and translations to a Django website(i18n). We will translate Python, JS code, as well as templates and Django-models. Plus, …

    Used termins


    Related questions