How to track product tour drop-off points
You built a 7-step product tour. Users start it. But something between step 3 and step 4 kills momentum, and you have no idea what. Without step-level tracking, your tour analytics are a black box: you know the completion rate, but not where users bail.
Most product tour libraries fire a single tour_completed event and call it a day. That tells you nothing about the 39% of users who started but never finished. The average product tour completion rate is just 61%, according to Chameleon's analysis of 15 million tour interactions. The fix isn't more events. It's the right events at the right granularity, piped into a funnel your team can actually read.
This tutorial walks through building step-level drop-off tracking with Tour Kit's @tour-kit/analytics package. You'll wire up step_viewed, step_completed, and tour_abandoned events, then visualize the funnel in PostHog or Mixpanel. By the end, you'll know exactly which step hemorrhages users and have the data to fix it.
npm install @tourkit/core @tourkit/react @tourkit/analyticsPrerequisites
- React 18+ or React 19 project
- A Tour Kit tour with 3+ steps already working
- An analytics tool account (PostHog, Mixpanel, Amplitude, or GA4)
- Basic familiarity with React hooks and TypeScript
What is product tour drop-off tracking?
Product tour drop-off tracking measures exactly where users stop progressing through a guided tour. Instead of treating the tour as a pass/fail binary, you instrument each step transition as a discrete event and compare how many users reach step N versus step N+1. The result is a conversion funnel specific to your tour, showing the precise step where engagement collapses and giving your team a clear target for improvement.
The formula is straightforward:
Step drop-off rate = ((Users at step N - Users at step N+1) / Users at step N) × 100A 5-step tour with 1,000 starters and 340 completions has a 66% cumulative drop-off rate. But that number is useless without knowing whether the loss happens at step 2 (bad targeting) or step 4 (confusing content). Step-level tracking gives you that answer.
Why drop-off tracking matters more than completion rate
Product tour drop-off tracking at the step level reveals problems that completion rate alone hides entirely, because a single aggregate number can't distinguish between a tour with one broken step and a tour that gradually loses users across every transition.
Completion rate is a vanity metric when used alone. A 74% completion rate on a 4-step tour sounds healthy until you discover that 38% of starters bail after the first screen. You're losing users before they see the value your tour is supposed to deliver.
Chameleon's benchmark data across 550 million tour interactions shows a striking pattern: 4-step tours average 74% completion, but 5-step tours crater to 34%. That single extra step cuts your completion rate in half. Without step-level drop-off data, you'd never know whether to shorten your tour or fix a specific step's content.
Here's what step-level data actually reveals:
| Metric | What it tells you | Action it enables |
|---|---|---|
| Completion rate alone | "Users finish or don't" | Nothing specific |
| Step-level drop-off | "Step 3 loses 40% of remaining users" | Rewrite step 3 content, add interactive element |
| Time-on-step | "Users spend 12s on step 2 vs 2s average" | Step 2 is confusing, so simplify or split it |
| Abandonment context | "Users close the tab during step 4" | Step 4's target element may be broken or off-screen |
As the Amplitude team puts it: "Data is the linchpin of any successful product tour. Without it, you're just guessing."
How to calculate drop-off rate (formula and example)
The per-step drop-off rate tells you what percentage of users who reached a given step failed to advance, and Userpilot's onboarding research shows that SaaS products typically see 30-50% cumulative drop-off across their onboarding flows, which means most tours have at least one step losing a significant chunk of users. Here's the formula applied to a realistic 5-step tour:
Step drop-off rate = ((Users at step N - Users at step N+1) / Users at step N) × 100Applied to a hypothetical onboarding tour with 1,000 starters:
| Step | Users reached | Drop-off rate | Diagnosis |
|---|---|---|---|
| 1. Welcome | 1,000 | 15% | Normal, some users dismiss immediately |
| 2. Create project | 850 | 8% | Healthy transition |
| 3. Invite team | 782 | 42% | Problem: users resist social actions early |
| 4. Run first test | 454 | 12% | Recoverable after step 3 fix |
| 5. View dashboard | 399 | — | Final step (39.9% overall completion) |
Step 3 is the problem. A 42% step-level drop-off where users are asked to invite teammates before they've seen the product's value. Moving that step to a secondary onboarding flow (or making it skippable) could recover hundreds of users per cohort.
Benchmark ranges based on Chameleon's 15M interaction dataset:
- Healthy step drop-off: under 15%
- Needs attention: 15-30%
- Critical: above 30%
Step 1: Set up the analytics provider
Tour Kit's @tour-kit/analytics package provides a plugin-based tracker that fires structured events for every tour lifecycle moment, including step_viewed, step_completed, tour_skipped, and tour_abandoned with step index and duration metadata attached automatically. Start by wrapping your app with the analytics provider and registering at least one plugin.
// src/providers/TourAnalytics.tsx
import { AnalyticsProvider } from '@tourkit/analytics'
import { PostHogPlugin } from '@tourkit/analytics/plugins/posthog'
const plugins = [
PostHogPlugin({
// PostHog SDK must be initialized before this runs
client: typeof window !== 'undefined' ? window.posthog : undefined,
}),
]
export function TourAnalyticsWrapper({ children }: { children: React.ReactNode }) {
return (
<AnalyticsProvider plugins={plugins}>
{children}
</AnalyticsProvider>
)
}Swap PostHogPlugin for MixpanelPlugin, AmplitudePlugin, or GoogleAnalyticsPlugin depending on your stack. Every plugin implements the same AnalyticsPlugin interface (track(), identify(), page()), and events dispatch to all registered plugins in order. Run a ConsolePlugin alongside PostHog during development to see events in your browser console.
Step 2: Instrument your tour with step-level events
Tour Kit's analytics tracker fires step_viewed and step_completed events automatically when you use the useAnalytics hook, but product tour drop-off tracking also requires capturing when users leave without finishing, which is where the tour_abandoned event comes in. Wire up your tour's onStepChange, onComplete, and onSkip callbacks to the tracker.
// src/tours/OnboardingTour.tsx
import { useTour } from '@tourkit/react'
import { useAnalytics } from '@tourkit/analytics'
export function OnboardingTour() {
const analytics = useAnalytics()
const tour = useTour({
tourId: 'onboarding-v2',
steps: [
{ id: 'welcome', target: '#welcome-banner', content: 'Welcome to Acme!' },
{ id: 'create-project', target: '#new-project-btn', content: 'Start by creating a project.' },
{ id: 'invite-team', target: '#invite-btn', content: 'Invite your teammates.' },
{ id: 'run-test', target: '#run-test-btn', content: 'Run your first test.' },
{ id: 'dashboard', target: '#dashboard-link', content: 'Check your results here.' },
],
onStepChange: (step, index, context) => {
// Fires on every step transition — the backbone of drop-off tracking
analytics.stepViewed(
'onboarding-v2',
step.id,
index,
context.tour.steps.length
)
},
onComplete: () => {
analytics.tourCompleted('onboarding-v2')
},
onSkip: (context) => {
// User clicked "Skip tour" — different from abandonment
analytics.tourSkipped(
'onboarding-v2',
context.currentStepIndex,
context.tour.steps[context.currentStepIndex]?.id
)
},
})
return <>{tour.component}</>
}The distinction between tour_skipped and tour_abandoned matters for your funnel. A skip is intentional (user clicked a button). Abandonment happens when the user navigates away, closes the tab, or the tour element disappears. Tour Kit detects abandonment via beforeunload and MutationObserver events automatically.
Step 3: Add time-on-step tracking for deeper analysis
Knowing where users drop off is step one, but knowing how long they hesitate before dropping gives you the "why," because a user who spends 2 seconds on a step and leaves had a fundamentally different problem than one who stares at it for 45 seconds. Tour Kit's tracker records duration on every event automatically (it starts a timer on stepViewed and stops it on stepCompleted or abandonment). You can enrich this with custom metadata:
// src/hooks/useDropOffTracking.ts
import { useCallback, useRef } from 'react'
import { useAnalytics } from '@tourkit/analytics'
export function useDropOffTracking(tourId: string) {
const analytics = useAnalytics()
const stepTimers = useRef<Map<string, number>>(new Map())
const onStepEnter = useCallback(
(stepId: string, stepIndex: number, totalSteps: number) => {
stepTimers.current.set(stepId, Date.now())
analytics.stepViewed(tourId, stepId, stepIndex, totalSteps, {
enteredAt: new Date().toISOString(),
percentComplete: Math.round((stepIndex / totalSteps) * 100),
})
},
[analytics, tourId]
)
const onStepExit = useCallback(
(stepId: string, stepIndex: number, totalSteps: number) => {
const enterTime = stepTimers.current.get(stepId)
const timeOnStep = enterTime ? Date.now() - enterTime : 0
analytics.stepCompleted(tourId, stepId, stepIndex, totalSteps, {
timeOnStepMs: timeOnStep,
timeOnStepSeconds: Math.round(timeOnStep / 1000),
})
stepTimers.current.delete(stepId)
},
[analytics, tourId]
)
return { onStepEnter, onStepExit }
}Now every step event carries timeOnStepMs in its metadata. When you build the funnel in PostHog or Mixpanel, you can break down drop-off by time spent, separating "confused users" (long dwell time, 15+ seconds) from "uninterested users" (instant dismissal, under 2 seconds).
Step 4: Build the funnel in your analytics tool
With step-level events flowing into your analytics backend, the next task is building a funnel chart that turns raw step_viewed and step_completed counts into the step-by-step drop-off visualization your product team can act on without needing to write SQL. Both PostHog and Mixpanel support ordered funnel analysis out of the box.
PostHog funnel setup
In PostHog, create a new Insight with type "Funnel":
- Add
step_viewedas the first event, filtered bytourId = onboarding-v2andstepIndex = 0 - Add another
step_viewedevent for each step index (1, 2, 3, 4) - Alternatively, use
step_completedevents if you want to track step exits rather than entries - Set the funnel window to 30 minutes (most tours complete in under 5 minutes, so this catches stragglers)
- Break down by
stepIdto see named steps instead of index numbers
PostHog's HogQL also lets you query step timing with raw SQL if the visual funnel isn't enough.
Mixpanel funnel setup
Mixpanel's funnel builder handles ordered step sequences natively:
- Create a new Funnel report
- Add
step_viewedevents in order, each filtered bystepIndex(0 through N) - Enable "time to convert" to see median time between steps
- Use "Flows" to discover where users actually go after dropping off (did they close the tab? Navigate to a different page? Start a different tour?)
Mixpanel's multi-path funnel feature is particularly useful here. It shows you what users did instead of completing the next step, not just that they left.
Step 5: Set up drop-off alerts
Tracking data that nobody looks at is worse than no tracking at all, because it gives you false confidence that you're measuring things while problems go unnoticed for weeks. Set a threshold alert so your team gets notified the moment a specific step's drop-off rate spikes above your baseline.
// src/analytics/drop-off-monitor.ts
import type { TourEvent, AnalyticsPlugin } from '@tourkit/analytics'
export function createDropOffMonitorPlugin(options: {
threshold: number // e.g., 0.3 for 30%
minimumSample: number // don't alert on tiny samples
onAlert: (alert: { stepId: string; dropOffRate: number }) => void
}): AnalyticsPlugin {
const stepCounts = new Map<string, { viewed: number; completed: number }>()
return {
name: 'drop-off-monitor',
track(event: TourEvent) {
if (!event.stepId) return
const counts = stepCounts.get(event.stepId) ?? { viewed: 0, completed: 0 }
if (event.eventName === 'step_viewed') counts.viewed++
if (event.eventName === 'step_completed') {
counts.completed++
const dropOffRate = 1 - counts.completed / counts.viewed
if (counts.viewed >= options.minimumSample && dropOffRate > options.threshold) {
options.onAlert({ stepId: event.stepId, dropOffRate })
}
}
stepCounts.set(event.stepId, counts)
},
}
}Register it alongside your main analytics plugin. The onAlert callback can post to Slack, PagerDuty, or your internal dashboard.
This runs client-side for real-time monitoring. For production alerting with larger sample sizes, run the aggregation server-side against your PostHog or Mixpanel data using their APIs.
Benchmarks: what good looks like
Industry benchmarks from Chameleon's published analysis of 550 million tour data points and Userpilot's onboarding research give you concrete targets for each metric, so you can tell whether your tour's drop-off pattern is normal or a sign of deeper UX problems. Here's what to aim for:
| Metric | Needs work | Acceptable | Strong |
|---|---|---|---|
| Overall completion rate | Below 40% | 40-65% | Above 65% |
| Per-step drop-off | Above 30% | 15-30% | Below 15% |
| First-step abandonment | Above 25% | 10-25% | Below 10% |
| Average time-on-step | Over 20s or under 1s | 3-12s | 5-8s |
| Tour length | 7+ steps (16% completion) | 5 steps (34%) | 3-4 steps (72-74%) |
The data point that should shape every tour you build: tours with 4 steps average 74% completion, but adding just one more step drops that to 34%. If your tour has more than 5 steps, split it.
Progress indicators boost completion by 12% and reduce dismissal by 20%. Click-triggered (self-serve) tours complete at 67% versus 31% for delay-triggered ones. Trigger method matters as much as content.
5 ways to improve your drop-off rate
After instrumenting product tour drop-off tracking and identifying your worst-performing steps, these five fixes consistently produce the largest improvements based on the benchmark data above and patterns we've observed testing Tour Kit's own onboarding flows.
1. Shorten your tour. If you have 5+ steps, split into a primary tour (3-4 steps covering the "aha moment") and a secondary tour triggered by a checklist or feature adoption nudge. Tour Kit's @tour-kit/checklists package handles this pattern natively.
2. Move social actions later. "Invite your team" steps consistently show the highest drop-off in onboarding tours. Users want to experience value before committing socially. Move these to a separate flow triggered after the user completes their first core action.
3. Add progress indicators. A visible step counter (3/5) gives users a sense of completion momentum. The 12% completion boost from progress indicators, as measured in Chameleon's dataset, is one of the easiest wins in onboarding.
4. Switch from delay triggers to click triggers. Self-serve tours (user clicks "Show me around") complete at more than double the rate of auto-triggered tours. If your tour fires on page load after a 3-second delay, test a button trigger instead.
5. Fix the content, not the format. When a specific step shows high time-on-step and high drop-off, the content is confusing, not the tour mechanism. Rewrite the copy, add a screenshot or GIF, or split the step into two simpler ones.
Tools for drop-off tracking
Tour Kit's @tour-kit/analytics package works with any analytics tool that accepts custom events, and the choice of analytics backend matters because funnel visualization quality varies significantly between tools. PostHog, Mixpanel, Amplitude, and GA4 all support ordered funnels, but their step-timing and path analysis features differ in ways that affect how actionable your drop-off data ends up being.
| Tool | Funnel support | Best for | Tour Kit plugin |
|---|---|---|---|
| PostHog | Ordered funnels, HogQL for custom queries | Self-hosted, privacy-first teams | PostHogPlugin |
| Mixpanel | Multi-path funnels, time constraints | Teams who need "what happened instead" analysis | MixpanelPlugin |
| Amplitude | Ordered/unordered funnels, cohort breakdown | Enterprise teams with complex segmentation needs | AmplitudePlugin |
| GA4 | Basic funnel exploration | Teams already on Google Analytics | GoogleAnalyticsPlugin |
For serious funnel work, PostHog or Mixpanel are the strongest choices. GA4's funnel exploration works but lacks the step-timing and path analysis features that make drop-off diagnosis actionable.
One honest limitation: Tour Kit doesn't include a built-in dashboard for tour analytics. You're responsible for the visualization layer. The upside is your tour data lives in your existing analytics stack alongside every other product metric, with no separate login, no data silo, and no per-MAU pricing surprises from a dedicated onboarding SaaS tool.
Common issues and troubleshooting
When setting up product tour drop-off tracking, these three issues come up most often during initial integration, and each one can make your funnel data look broken even when the events are firing correctly.
"Events fire but the funnel shows 0 conversions"
Check your funnel's time window. If your funnel requires all steps within 5 minutes but some users take 15 minutes (they got distracted mid-tour), widen the window to 30 minutes or 1 hour. Also verify that the tourId property matches exactly across all events. A typo in one step breaks the entire funnel.
"Step 1 shows fewer users than tour_started"
This happens when the tour starts but the first step's target element hasn't rendered yet. Tour Kit waits for the element by default, but if your component uses React.lazy, the step_viewed event fires only after the element appears. The gap between tour_started and the first step_viewed is your loading latency.
"Abandonment events fire on every page navigation"
If you're using a SPA router (Next.js App Router, React Router), Tour Kit's beforeunload handler may fire on route changes within the app. Configure the analytics provider to distinguish between hard navigation (actual abandonment) and soft navigation (SPA route change during the tour). Check that your tour's step targets exist on the destination route.
Next steps
With step-level product tour drop-off tracking in place and funnel visualization showing exactly where users bail, the next layer is acting on the data automatically rather than manually reviewing dashboards every week.
- A/B test tour variants: use Tour Kit's
@tour-kit/schedulingpackage with feature flags to test different step orders and measure which variant has lower drop-off. We covered this in detail in our A/B testing product tours guide. - Trigger re-engagement flows: when a user drops off at a specific step, fire a
tour_abandonedevent that triggers a follow-up checklist or hint using@tour-kit/checklistsor@tour-kit/hints. - Track post-tour activation: tour completion doesn't equal goal completion, as Product Fruits notes. Connect your tour funnel to your product's activation metric (first project created, first test run, first invite sent) to measure whether the tour actually works, not just whether users finish it.
For a working example with all the tracking code wired up, check the Tour Kit documentation and the analytics package API reference.
FAQ
What is a good product tour completion rate?
Tour Kit users and industry benchmarks from Chameleon's 15-million-interaction dataset show that 3-4 step tours average 72-74% completion. Tours with 5 steps drop to 34%, and 7+ step tours complete at just 16%. A reasonable target is above 65% for short tours and above 40% for longer onboarding sequences. Product tour drop-off tracking at the step level is the only way to know which specific steps need improvement.
How do I track which tour step users drop off at?
Fire a step_viewed event on every step transition and a step_completed event when the user advances. Compare the counts in a funnel chart — the step with the largest gap between step_viewed and the next step's step_viewed is your drop-off point. Tour Kit's @tour-kit/analytics package handles this automatically with stepViewed() and stepCompleted() tracker methods.
Does adding analytics to a product tour affect performance?
Tour Kit's analytics events are lightweight JSON payloads batched in a queue. The @tour-kit/analytics package uses a flush interval (default: 5 seconds) and sends events asynchronously. In our testing, enabling analytics adds less than 1ms to each step transition. The analytics package itself is under 3KB gzipped, so the bundle size impact is minimal.
What's the difference between tour skip and tour abandonment?
A skip happens when the user intentionally clicks a "Skip tour" or "Close" button — Tour Kit fires tour_skipped with the step index where they bailed. Abandonment occurs when the user navigates away, closes the browser tab, or the tour's target element disappears from the DOM. Tour Kit fires tour_abandoned for these cases. Separating the two in your funnel reveals whether users are actively rejecting the tour (skip) or passively disengaging (abandonment).
Can I track product tour drop-off without a third-party analytics tool?
Yes. Tour Kit's plugin interface accepts any object with a track() method. You can write a custom plugin that sends events to your own API endpoint, stores them in localStorage for local analysis, or logs them to the console during development. The ConsolePlugin included with @tour-kit/analytics is useful for debugging your event schema before connecting a production analytics tool.
Related articles

Behavioral triggers for product tours: event-based onboarding
Build event-based product tours that trigger on user actions, not timers. Code examples for click, route, inactivity, and compound triggers in React.
Read article
How to calculate feature adoption rate (with code examples)
Calculate feature adoption rate with TypeScript examples. Four formula variants, React hooks, and benchmarks from 181 B2B SaaS companies.
Read article
Cohort analysis for product tours: finding what works
Build cohort analysis around product tour events to measure retention impact. Step-level tracking, trigger-type segmentation, and Tour Kit code examples.
Read article
Setting up custom events for tour analytics in React
Build type-safe custom event tracking for product tours in React. Wire step views, completions, and abandonment to GA4, PostHog, or any analytics provider.
Read article