Product tour alternatives: a checklist that resumes
A product tour is a script with no memory. The alternatives — a resumable checklist, a contextual bubble, the empty state — and the one number to choose with.
A product tour is usually the first answer to "new users do not know what to do". It is a fair answer: you highlight the interface, in order, with the user's own screen behind the overlay. What it is not is a state machine, and that detail decides whether it is the right mechanism for your product. The alternatives answer a different question, at a different moment.
What a product tour is actually made of
Every guided tour, whether you write it yourself or install a library, is a list of anchored steps: point at an element, say something, move on. Three assumptions are built into that shape.
- The user starts at the beginning, at a moment you chose.
- The user follows your order, not their own.
- The user finishes in one sitting, in one tab.
When all three hold, a tour is the most direct teaching tool you have, and its steps can afford to be long, because the user is on rails.
Notice what the list does not contain: a record of what the user did. In most tour libraries the position in the sequence lives in the browser tab. Close it and the next visit starts again; sign in elsewhere and nothing is remembered, because nothing was stored. That is a design choice, not a defect, and it is the property you feel on day three.
Where the script stops working
A real first session is not eight uninterrupted steps. There is an email to confirm, a password manager to open, a redirect to a payment provider, a phone call in the middle of it. The user leaves at step three with the tab open on a laptop that goes to sleep, and comes back tomorrow. A tour that cannot resume does not merely fail to teach — it restarts, which reads as a product that forgot them.
The anchor is not there yet. A step that points at a table or a chart assumes content. A brand-new account has an empty table, so the step highlights the wrong thing or nothing at all. Tours get authored against a demo account that looks nothing like a first session.
The order is yours. A developer who signed up to connect a webhook does not want four steps before the one they came for. A linear script offers one way out — skip the whole thing — which is the same as giving up on onboarding.
What a tour lets you measure is how many people clicked through it. That is activity. The number you want is your activation event: the moment a new account first gets value. A click on an overlay does not record it.
The alternatives, and the question each one answers
- A persistent checklist. A panel, or a peek on a small screen, that lists what is left, with progress stored server-side against the person. It resumes across sessions, tabs and devices, and it can be dismissed until the next visit or permanently — two different signals, both recorded. It is the only mechanism here that answers "what do I still owe you".
- A contextual bubble. One hint, at the moment it applies: on this URL, or after this event, anchored to the element it describes. The strength is timing — it interrupts less than a tour because it does not fire at signup. The limit is coverage: one trigger teaches one thing.
- The empty state. The cheapest alternative and the most under-used. Replace "no projects yet" with the first action, its button, and one sentence on why it is worth doing. It reaches everyone, including the users who dismissed your checklist.
- A message outside the app. An email or an in-app message for the person who is not at the screen: good as a follow-up to an unfinished list, weak as a first contact.
- Documentation. For a developer product, a quick start a human can read is frequently the entire onboarding. A checklist whose first step links to that page is the compatibility layer between the two.
Clicking a step is not doing the step
If you keep in-app guidance, keep those two facts apart in your data. A click dispatches gs:step-clicked with the step key and its URL, then lets the link navigate — the SDK cannot know whether the user arrived, let alone whether they did anything there. Completion comes from one of two places:
- From the client, when the action happened in the same session:
gs.complete('first_project'). - From your backend, when only the server can see it — an import finished, a webhook was delivered, an invite was accepted:
POST /api/gs/v1/server/completewith the step key and the project's secret key. Those rows carry sourceserver, so backend-driven progress stays distinguishable from clicks.
The embed you would type
Mount the element once in the layout your authenticated app already has, and report the click separately from the result:
<script src="https://gettingstarted.marthaia.com/sdk/v1/gs.min.js" defer></script>
<getting-started
endpoint="https://gettingstarted.marthaia.com"
project-key="pk_your_publishable_key"
user-id="u_1829"></getting-started>
<script>
document.querySelector('getting-started')
.addEventListener('gs:step-clicked', (e) => {
// they went looking — this is not proof they found it
track('onboarding_step_clicked', { key: e.detail.key })
})
</script>
# proof lives on the server: the import really finished
curl -X POST https://gettingstarted.marthaia.com/api/gs/v1/server/complete \
-H "X-GS-Secret: sk_you..._key" \
-H "Content-Type: application/json" \
-d '{"userId":"u_1829","stepKey":"first_project"}'
Choose with one number, not with taste
Write your activation event down first and make it the last step. The number that decides between a tour and a checklist is then the share of new accounts that reach it in their first session, counted from completion records rather than clicks. The project stats endpoint (GET /api/gs/v1/admin/projects/{id}/stats) returns subjects, completions per step key and completedAll, so completedAll over subjects is a real figure you can watch move. No public benchmark says what that share should be, so your own pre-change number is the baseline.
To compare the mechanisms honestly, split new accounts, give each arm one, and read the same activation rate per arm once enough accounts have landed — never the click-through on the overlay, which will always be flattering.
Moving the steps you already have
Both mechanisms are lists of steps, so the migration rewrites the triggers, not the writing. Keep your step keys: your completion calls and your analytics events already carry them. Put labels, descriptions and call-to-action text in the console rather than in the bundle, so a copy change is not a deploy. Then preview the list before anyone sees it — the preview drives the real widget from the list you are editing, with no network calls, which is the fastest way to notice that step four stopped making sense once you dropped the overlay.
The event surface to hang your analytics on is in the docs.