Creating an engaging and interactive tour is a powerful way to onboard users, highlight key features, and enhance the user experience in your React application. 🌟 Whether you’re introducing new functionality, guiding first-time users, or providing contextual tips, a well-designed interactive tour can leave a lasting impression. 💡 In this complete guide, we’ll walk you through the process step by step—from planning and designing your tour to implementing it with best practices. 🛠️ By the end, you’ll have all the tools and knowledge needed to create a polished, user-friendly tour that makes your app shine and keeps your users coming back for more. 🎉
To achieve this, we need to create a Tour
component. This component will accept a list of steps and navigate through them sequentially.
Define Interfaces
When defining type interfaces for each step, a query selector is used to locate the area that will be highlighted. Additionally, each step includes a title for the description box and content for the description itself.
// Tour.tsx
export interface ITourStep {
q: string; // Query selector string
title: string; // Title for the description box
content: ReactNode; // Content to be displayed as the description
}
interface IProps {
items: ITourStep[] | null; // List of tour steps. null indicates inactive state
onComplete: () => void; // Callback to trigger on tour finished
}
export default function Tour({ steps, onComplete }: IProps) {
...
}
Add Elements
There are two key elements: one focuses
on highlighting the target area while dimming the surrounding region, and the other is the bubble
that displays the title, description, and buttons.
// Tour.tsx
...
export default function Tour({ steps, onComplete }: IProps) {
const focus = useRef<HTMLDivElement>(null!);
const bubble = useRef<HTMLDivElement>(null!);
const content = useRef<HTMLSpanElement>(null!);
const [index, setIndex] = useState(0); // active index
...
return (
<>
<div
ref={focus}
className="pointer-events-none fixed left-0 top-0 z-[99] hidden size-56 rounded-lg border-2 border-white/10 opacity-0 shadow-[0_0_0_9999px_#000000f2]"
/>
<div
ref={bubble}
className="fixed left-0 top-0 z-[100] hidden w-64 flex-col gap-3 rounded-xl bg-white p-4 opacity-0"
>
<span ref={content} className="text-justify text-sm text-black" />
<button onClick={onNext} className="text-green-400">
{!!active && active < items.length ? "Next" : "Done"}
</button>
</div>
</>
);
}
Final Code
The final step is to add animations and functionality to the component. This includes displaying step 1 when the steps change, updating the current step when the "Next" button is clicked, and hiding the tour when the steps are set to null
. Additionally, we need to ensure the focused area scrolls into view and that the bubble is positioned optimally, adapting to all screen sizes for a responsive design.
// Tour.tsx
"use client";
import { useGSAP } from "@gsap/react";
import clsx from "clsx";
import gsap from "gsap";
import { TextPlugin } from "gsap/all";
import { useRef, useState } from "react";
gsap.registerPlugin(TextPlugin);
export interface ITourStep {
q: string; // Query selector string
title: string; // Title for the description box
content: ReactNode; // Content to be displayed as the description
}
interface IProps {
items: ITourStep[] | null; // List of tour steps. null indicates inactive state
onComplete: () => void; // Callback to trigger on tour finished
}
export default function Tour({ steps, onComplete }: IProps) {
const focus = useRef<HTMLDivElement>(null!);
const bubble = useRef<HTMLDivElement>(null!);
const content = useRef<HTMLSpanElement>(null!);
const [index, setIndex] = useState(0); // active index
useGSAP(async () => {
const inactive = !index || !steps || index > steps.length;
gsap.to([bubble.current, focus.current], {
opacity: inactive ? 0 : 1,
display: inactive ? "non" : "flex",
duration: 0.7,
});
if (inactive) return;
const step = steps[index - 1];
const el = document.querySelector(step.q);
if (!el) return;
el.scrollIntoView({ block: "center", inline: "start" });
const { width: bw } = bubble.current.getBoundingClientRect();
const { width, height: h0, x, y } = el.getBoundingClientRect();
const [vw, vh] = [window.innerWidth, window.innerHeight];
const sm = vw < lg;
const height = h0 > vh ? vh - oy2 : h0;
const right = x + width / 2 > vw / 2;
const bigH = height > vh / 2;
const bx = sm ? -ox : !right ? width + ox2 : -(bw + ox2);
const by = sm ? (!bigH ? height + oy2 : height / 2 + oy2) : -oy;
gsap.to(bubble.current, { left: x, top: y, x: bx, y: by });
const [w, h] = [width + ox2, height + oy2];
const [fx, fy] = [x - ox, y - oy];
gsap.to(focus.current, { width: w, height: h, left: fx, top: fy });
gsap.to(content.current, { text: step.content });
}, [index, steps]);
const onNext = () => {
if (index > steps.length - 1) return setIndex(0);
setIndex((i) => i + 1);
};
return (
<>
<div
ref={focus}
className="pointer-events-none fixed left-0 top-0 z-[99] hidden size-56 rounded-lg border-2 border-white/10 opacity-0 shadow-[0_0_0_9999px_#000000f2]"
/>
<div
ref={bubble}
className="fixed left-0 top-0 z-[100] hidden w-64 flex-col gap-3 rounded-xl bg-white p-4 opacity-0"
>
<span ref={content} className="text-justify text-sm text-black" />
<button onClick={onNext} className="text-green-400">
{!!active && active < items.length ? "Next" : "Done"}
</button>
</div>
</>
);
}
Usage
const tour: ITourStep[] = [
{
q: "#box-1",
title: "Title 1",
content: "Content 1",
},
{
q: "#box-2",
title: "Title 2",
content: "Content 2",
},
];
export default function App() {
const [steps, setSteps] = useState<ITourStep[] | null>(null);
const onStart = () => setSteps(tour);
const onComplete = () => setSteps(null);
return (
<div id="home">
<div id="box-1">...</div>
<div id="box-2">...</div>
<button onClick={onStart} />
<Tour steps={steps} onComplete={onComplete} />
</div>
);
}
🎉 Congratulations! 🎉
You’ve successfully built an amazing interactive tour in React! 🚀 By following this guide, you’ve created a user-friendly feature that not only enhances your app's usability but also delivers a delightful experience for your users. From planning the steps to implementing animations and responsive design, you've covered everything needed to make your tour both functional and engaging.
Now it’s time to take your app to the next level. Whether you're onboarding new users or guiding them through advanced features, your interactive tour is ready to shine! ✨
Thank you for reading, and happy coding! 💻🎨
Top comments (0)