Home >Web Front-end >JS Tutorial >Designing and Implementing Ant Design Global App Tour for React Apps.
User tours are an invaluable usability feature for web applications. They allow you to onboard new users effectively, providing step-by-step guides to help them understand the software. Tours can also serve as a quick reference for recurring tasks or advanced features.
We aim to create a solution that allows you to create onboarding experience that span across multiple pages in the react application. Here is how it looks :
Ant Design provides a Tour component to create interactive guides. However, it has some limitations:
Here’s an example from the official documentation that demonstrates a basic local implementation:
import React, { useRef, useState } from 'react'; import { EllipsisOutlined } from '@ant-design/icons'; import { Button, Divider, Space, Tour } from 'antd'; const App = () => { const ref1 = useRef(null); const ref2 = useRef(null); const ref3 = useRef(null); const [open, setOpen] = useState(false); const steps = [ { title: 'Upload File', description: 'Put your files here.', target: () => ref1.current }, { title: 'Save', description: 'Save your changes.', target: () => ref2.current }, { title: 'Other Actions', description: 'Click to see other actions.', target: () => ref3.current }, ]; return ( <> <Button type="primary" onClick={() => setOpen(true)}>Begin Tour</Button> <Divider /> <Space> <Button ref={ref1}>Upload</Button> <Button ref={ref2} type="primary">Save</Button> <Button ref={ref3} icon={<EllipsisOutlined />} /> </Space> <Tour open={open} onClose={() => setOpen(false)} steps={steps} /> </> ); }; export default App;
While this implementation works well for single pages, it falls short in scenarios where tours span across pages in your React application.
Here’s how we implement this:
Pre steps , app.jsx, routes.jsx, routesNames.js :
import { RouterProvider } from "react-router-dom"; import AppRouter from "./routes"; export default function App() { return <RouterProvider router={AppRouter} />; }
export const ROUTE_NAMES = { HOME: "/", ABOUT: "/about", };
import AppLayout from "./AppLayout"; import { createBrowserRouter } from "react-router-dom"; import { ROUTE_NAMES } from "./routeNames"; import { Home } from "./components/Home"; import { About } from "./components/About"; import { Result } from "antd"; import {TourProvider} from "./TourContext"; const GetItem = (label, key, icon, to, children = [], type) => { return !to ? { key, icon, children, label, type, } : { key, icon, to, label, }; }; const GetRoute = (path, element, params = null) => { return { path, element, }; }; const WithAppLayout = (Component) => <TourProvider><AppLayout>{Component}</AppLayout></TourProvider>; export const routeItems = [ GetItem("Home", "home", null, ROUTE_NAMES.HOME), GetItem("About", "about", null, ROUTE_NAMES.ABOUT), ]; const AppRouter = createBrowserRouter([ GetRoute(ROUTE_NAMES.HOME, WithAppLayout(<Home />)), GetRoute(ROUTE_NAMES.ABOUT, WithAppLayout(<About />)), GetRoute( "*", <Result status="404" title="404" subTitle="Sorry, the page you visited does not exist." /> ), ]); export default AppRouter;
We use React Context to manage the tour's global state, including the active tour steps.
import React, { createContext, useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { APP_TOURS } from "./steps"; const TourContext = createContext(); export const TourProvider = ({ children }) => { const [isTourActive, setTourActive] = useState(false); const navigate = useNavigate(); useEffect(() => { if (isTourActive) { navigate("/home"); // Redirect to the starting point of the tour } }, [isTourActive, navigate]); return ( <TourContext.Provider value={{ isTourActive, setTourActive, steps: APP_TOURS }}> {children} </TourContext.Provider> ); }; export default TourContext;
Instead of React refs, we use querySelector to dynamically fetch elements by a custom data-tour-id attribute.
const getTourStepElement = (id) => document.querySelector(`[data-tour-id="${id}"]`); export const APP_TOURS = { "/home": [ { title: "Upload File", description: "Put your files here.", target: () => getTourStepElement("upload") }, { title: "Save", description: "Save your changes.", target: () => getTourStepElement("save") }, { type: "navigate", to: "/about", title: "About Us", description: "Learn more about us." }, ], "/about": [ { title: "About Us", description: "Here's what we are all about.", target: () => getTourStepElement("about") }, ], };
This component dynamically handles navigation and steps across pages.
import React, { useContext } from "react"; import { Tour } from "antd"; import { useNavigate } from "react-router-dom"; import TourContext from "./TourContext"; export const GlobalTour = () => { const { isTourActive, steps, setTourActive } = useContext(TourContext); const navigate = useNavigate(); return ( <Tour open={isTourActive} onClose={() => setTourActive(false)} steps={steps} onChange={(current) => { const step = steps[current]; if (step.type === "navigate") { navigate(step.to); } }} /> ); };
The tour is seamlessly integrated into the layout, accessible from any page.
import React, { useContext } from "react"; import { Layout, Button } from "antd"; import { Link } from "react-router-dom"; import TourContext from "./TourContext"; import { GlobalTour } from "./GlobalTour"; const { Header, Content, Footer } = Layout; const AppLayout = ({ children }) => { const { setTourActive } = useContext(TourContext); return ( <Layout> <Header> <Link to="/home">Home</Link> <Link to="/about">About</Link> <Button onClick={() => setTourActive(true)}>Start Tour</Button> </Header> <Content>{children}</Content> <Footer>© {new Date().getFullYear()} My App</Footer> <GlobalTour /> </Layout> ); }; export default AppLayout;
Since our tour span across multiple pages , we will assig data-tour-id for each component we want to highlight in our steps
import { Button, Space } from "antd"; import { EllipsisOutlined } from "@ant-design/icons"; export const Home = () => { return ( <> <Button data-tour-id="upload" >Upload</Button> <Button data-tour-id="save" type="primary"> Save </Button> <Button data-tour-id="actions" icon={<EllipsisOutlined />} /> </> ); };
export const About = () => { return <div data-tour-id="about">About</div>; };
The above is the detailed content of Designing and Implementing Ant Design Global App Tour for React Apps.. For more information, please follow other related articles on the PHP Chinese website!