How to build a product tour that survives your next deploy

How to build an in-app product tour that guides new users to activation, anchors to real UI, survives route changes and stays cheap to update after a release.

How to build a product tour that survives your next deploy

A product tour that ships weekly has thirteen chances a quarter to break: an anchor moves, a label changes, and the pointer ends up floating over empty space.

So the question is less how to build a product tour than whether the tour you build will survive the next deploy. This guide is about keeping an in-app tour short, contextual and tied to one activation moment, and making sure a UI change does not turn it into a support ticket. It is about tours for users who have already signed up. The last section covers where that ends and a website demo begins.

When does a product tour earn its place?

A tour earns its place when the user has to do something they cannot work out from context, and it takes more than one step across more than one element. A single tooltip works for one unfamiliar element with no sequence. A checklist works for several independent tasks in any order. If the product explains itself, extra UI chrome gets in the way.

A tour that explains five features to a user who only needs to send their first invite is friction with a progress bar.

What is the activation moment the tour should end on?

It is the one event that defines a successful start for your product: sending the first invite, publishing the first project, connecting the first data source. Name it before you write a single step, and end the tour there instead of at the end of your feature list. A tour that keeps going after activation is teaching someone who already succeeded.

What goes in the smallest useful flow?

One user, one job, one path to one outcome. Leave branching out of the first version. "If you are an admin, go here; if you are an end user, go there" doubles the maintenance, because every UI change now has to be fixed in two places. A short linear flow also reads like help. A branching one reads like a decision tree the user never asked for.

First drafts collect junk: a welcome modal about the company's mission, a "here is what you can do" feature tour, copy like "let's get started," and steps that describe the UI instead of telling the user what to do. Cut anything that does not move the user toward the activation event. Every step should ask for something specific.

How do you anchor each step to a real element?

Anchor to a stable attribute you control, not to a coordinate, a CSS class that changes in a refactor, or a selector that matches three things. In a React or Next.js app, that means a data-tour attribute on the target, such as data-tour="send-invite". The tour engine looks up elements by that attribute to position each tooltip. It survives refactors and does not break when a className changes or the layout shifts. Use meaningful names like send-invite, connect-source and publish-project so they read well in both the DOM and the tour config.

Loose selectors are the most common reason tours break silently. A step anchored to the third item in a nav list breaks when someone adds a nav item. A hardcoded pixel offset breaks when the viewport changes. Text-based anchors break when the copy is edited. The tour does not error. It lands on the wrong element, or nowhere. If an element matters enough to tour, it gets a stable attribute, and that attribute is handled like an API.

How do you make the tour survive route changes in a single-page app?

Make the tour wait until the right route is active and the target element is mounted. In a single-page app, navigation swaps the component tree without reloading the page, so a step can be waiting to render while the element it needs has not mounted yet, has unmounted, or reappears after an async fetch. The tour does not know any of that unless you tell it.

Track which step the user is on, which route that step lives on, and whether the target is in the DOM. On a route change, check all three before rendering. If the element is not there, wait for it with a MutationObserver or a route-readiness callback, not a setTimeout.

For re-entry after a refresh or back navigation, store the step index outside the UI, in localStorage or the user's server-side record. On load, read it, navigate to the right route and resume. Restarting from step one after a refresh is a fast way to get the tour dismissed for good.

What should each tour step say?

Each step needs three things: what to do, where to do it, and why it matters right now. "Click Send invite to add your first teammate. You need one before you can share projects." That is a complete step. It does not need a headline, a product name or a sentence starting with "Welcome to." Keep it short, under 25 words as a working limit. A step that needs more is doing too much, so split it or cut it.

If different users need different tours, segment by the job they have to do first, not by role label. An admin and a developer may have different activation events. If they have the same first job, give them the same tour. If they differ, give them different tours rather than one branching tour. A single tourId passed at initialization keeps the engine the same and puts the difference in config, not in component logic.

Where should tour state live?

In one place: the user's server-side record, or a single client-side module that owns every read and write. A tour needs five states: not started, in progress, completed, snoozed, and eligible for re-entry. Snoozed with no way back means someone who dismissed the tour on a bad day never gets a second chance. Completed with no replay means someone who wants to revisit a step has nowhere to go.

State scattered across a cookie, a window variable and a one-off localStorage key becomes a maintenance trap. When you add a step or move the activation event, you have to update every place that wrote state, and missing one means users see a tour they already finished.

How do you test a product tour before users see it?

Walk it through in staging with a checklist, then check it against real states. The checklist:

  • Every step renders on the correct route.
  • Every tooltip sits against the right element.
  • Dismissal works at every step.
  • Keyboard navigation moves through the steps without a mouse.
  • Re-entry from a refreshed page lands on the right step.
  • Snoozed and completed states persist across sessions.

Accessibility belongs in the first build. The W3C's guidance on focus order says a modal dialog should take focus when it opens and return it to the trigger when it closes. A tour step behaves like a dialog, so if focus does not move into it, keyboard and screen reader users get stuck.

Then test against real states, not a mock. Use a fresh account with no data, an account mid-onboarding, and an account that already finished the tour. A feature flag that is off or a slow data fetch can break a step that passed on the happy path in staging.

How do you measure whether a tour works?

Measure the activation event, not tour completion. Track tour started, each step completed, tour completed, and the downstream activation event. Completion tells you whether people finished. Activation tells you whether behavior changed. A tour that almost everyone completes and that moves nothing downstream is one people clicked through to make it go away.

The cleanest read is a split: show the tour to half of new users and compare activation between the two groups. If you cannot run that, compare activation before and after the tour shipped, keeping an eye on cohort size and traffic source. Completers activating more than non-completers is only a hint, since people who finish a tour may already be more motivated.

What should trigger the tour?

Real behavior, not page load. Firing on page load fires for returning users, for people who refreshed, and for people in the middle of something else, and the tour turns into noise. A better trigger is a condition such as onboarding_tour_eligible, true only when the user has signed up, has not reached the activation milestone and has not dismissed the tour. Fire it once, on the first meaningful interaction after signup.

Hold it back if the user is already moving. Someone who signs up and starts creating a project does not need interrupting. An example event map: signup_completed starts an eligibility window, a first_session_idle event fires when the user has not acted for a few seconds, and that fires the tour.

How do you keep a tour cheap to update?

Decide before the product ships what each kind of change will cost. Keep the step config in one file, reviewed with the UI change that touches it, and agree a simple rule set.

  • Label or copy change only: edit the step text in place.
  • Layout change that moves an element: fix the anchor for that step. One step, not the whole flow.
  • Flow change that adds or removes a step: rebuild the affected section as a small project with a defined scope.

Without rules, every UI change is a judgment call, and those get pushed off until a user hits a stale step.

Where does a product tour stop and an AI demo agent start?

An in-app tour serves people who have already signed up. A demo agent serves people who have not. Everything above is about getting a new user to their first win inside the product. It does nothing for the visitor on your marketing site who wants to see what the product does before creating an account.

That visitor is the one Inkly serves. Inkly is the AI demo agent that turns website visitors into buyers. It asks the visitor's role and goal, guides them through the product live, answers questions in context, and hands buyer intelligence to sales. How it works walks through the three stages, and what an interactive demo is covers the click-through format a visitor might meet instead. Vercel's write-up of its virtual product tour is a public example of the same need: it lets prospects interact with the product before account creation.

Every Inkly plan includes the voice and chat demo agent, and plans are priced by active visitors, where one person counts once a month. Free is $0 for 30 active visitors. Pro starts at $150 a month ($120 a month billed yearly) for 150 active visitors, and adds visitor details, tailoring and qualification. Enterprise is custom and adds a clickable clone of your product, live product streaming and the CLI. See the pricing page. If your growth problem is activation after signup, a tour like the one in this post is the right tool and an agent is not.

FAQ

What is the simplest product tour structure that helps new users reach activation?

One user, one job, one path to one activation event. It starts at the first action the user has to take and ends when they complete the activation event. Every step in between asks for something specific.

How do I decide between a tour, a tooltip, a checklist or nothing?

Use a tour for a sequence across several elements that users cannot work out from context. Use a tooltip for one unfamiliar element. Use a checklist for independent tasks in any order. Use nothing when the product explains itself.

What should you do this week?

Name your activation event. Build one linear flow that ends on it. Wire one anchor with a stable data-tour attribute, and test it in staging against a fresh account before you add a second step. A tour worth building is short, anchored to real elements, stateful outside the UI and measured against activation, not completion.

Want to see the steps behind an agent-led demo? Here is how Inkly works, stage by stage.

Try Inkly

Ship your next demo before the meeting starts

Interactive demos built from your real product and kept current as you ship, done for you.

Book a demo

Keep reading

All posts →