Google Analytics 4 + Tour Kit: event tracking for onboarding
You shipped a product tour. Users click through it. But do they finish? And does finishing predict whether they stick around?
GA4 doesn't ship a recommended event for product tours. No tutorial_begin equivalent covers multi-step onboarding flows with branching logic and skip patterns. You need custom events, and over 60% of GA4 implementations have configuration issues that produce unreliable data (Tatvic Analytics, 2026). The usual approach, scattering window.gtag() calls through your tour components, gets messy fast and breaks silently when parameters change.
Tour Kit's @tour-kit/analytics package takes a different approach: a plugin interface that maps 6 tour lifecycle events to GA4 custom events automatically. Configure it once, and every tour in your app gets instrumented.
GA4 is installed on 37.9 million websites as of 2026 (SQ Magazine), so odds are good your app already has it loaded. This tutorial shows how to wire them together, build a completion funnel in GA4 Explorations, and dodge the gotchas.
npm install @tourkit/core @tourkit/react @tourkit/analyticsWhat you'll build
By the end of this tutorial you'll have a Tour Kit onboarding flow that sends structured GA4 custom events for every tour interaction: start, step view, step complete, skip, and finish. Those events feed a GA4 Funnel Exploration showing exactly where users drop off. The integration uses Tour Kit's built-in Google Analytics plugin, so you won't write any gtag() calls yourself. About 40 lines of TypeScript, 3 files, and zero additional dependencies beyond what GA4 already loads.
One thing to know upfront: Tour Kit requires React 18.2+ and doesn't have a visual builder. You write tour steps in code. If your team needs a drag-and-drop editor, this isn't your tool.
Prerequisites
- React 18.2+ or React 19
- GA4 property with a Measurement ID (
G-XXXXXXXXXX) gtag.jsloaded on the page (Google Tag Manager or the script tag)- Tour Kit installed (
@tourkit/core+@tourkit/react) - A working product tour with at least 3 steps
If you don't have a tour yet, the Next.js App Router tutorial covers setup from scratch.
Step 1: Install the analytics package
Tour Kit splits analytics into its own @tour-kit/analytics package (under 3KB gzipped) so apps that don't need event tracking pay zero bundle cost. The GA4 plugin inside it adds no extra dependencies beyond what's already on your page, since it calls the gtag() function from Google's script tag or Tag Manager snippet. Install it alongside your existing Tour Kit packages.
npm install @tourkit/analyticsThe plugin architecture means you can swap GA4 for PostHog, Mixpanel, or Amplitude later without touching your tour code. But for this tutorial we're focused on GA4 alone.
Step 2: Configure the GA4 plugin
Tour Kit's googleAnalyticsPlugin wraps window.gtag() with typed tour events and prefixes every event name with tourkit_ by default, so your tour events don't collide with the rest of your GA4 namespace. The prefix is configurable if you prefer a different convention. Setup takes about 8 lines of TypeScript.
// src/lib/analytics.ts
import { createAnalytics } from '@tour-kit/analytics'
import { googleAnalyticsPlugin } from '@tour-kit/analytics/google-analytics'
export const analytics = createAnalytics({
plugins: [
googleAnalyticsPlugin({
measurementId: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID!,
eventPrefix: 'tourkit_', // events appear as tourkit_tour_started, etc.
}),
],
debug: process.env.NODE_ENV === 'development',
})With debug: true in development, every event logs to the console before hitting GA4. Catches silent failures early. As one team reported: "We spent three weeks setting up custom events in GA4. Two months later, half of them were broken and nobody noticed" (KISSmetrics).
This configuration sends these GA4 custom events:
| Tour Kit event | GA4 event name | Parameters sent |
|---|---|---|
tour_started | tourkit_tour_started | tour_id, total_steps |
step_viewed | tourkit_step_viewed | tour_id, step_id, step_index, total_steps |
step_completed | tourkit_step_completed | tour_id, step_id, step_index, duration_ms |
tour_completed | tourkit_tour_completed | tour_id, total_steps, duration_ms |
tour_skipped | tourkit_tour_skipped | tour_id, step_index, total_steps |
tour_abandoned | tourkit_tour_abandoned | tour_id, step_index, duration_ms |
That's 6 custom event names. GA4 allows 500 unique event names per property (Google GA4 docs), so tour events consume just over 1% of your budget. Worth planning for if you're running multiple tours across a SaaS product with dozens of other custom events.
Step 3: Wire analytics into your tour provider
Wrap your component tree with AnalyticsProvider above TourKitProvider. This wires the GA4 plugin into every tour in your app automatically, with zero per-tour configuration required. The provider uses React context internally, so it works with code-splitting and lazy-loaded routes without extra setup.
// src/app/providers.tsx
'use client'
import { TourKitProvider } from '@tourkit/react'
import { AnalyticsProvider } from '@tourkit/analytics'
import { analytics } from '@/lib/analytics'
export function Providers({ children }: { children: React.ReactNode }) {
return (
<AnalyticsProvider analytics={analytics}>
<TourKitProvider>
{children}
</TourKitProvider>
</AnalyticsProvider>
)
}Then wrap your layout:
// src/app/layout.tsx
import { Providers } from './providers'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}From this point, every <TourProvider> inside the tree sends events through the GA4 plugin. No extra props needed on individual tours. Run 5 tours or 50, the same 2-line provider setup covers all of them.
Step 4: Verify events in GA4 DebugView
GA4's standard reports take 24-48 hours to process new event data, so you can't rely on them during development. DebugView shows events in real time as they arrive from your browser. Explorations update in 4-8 hours. For initial verification, DebugView is the only practical option.
- Open GA4 Admin > Data Display > DebugView
- Install the Google Analytics Debugger Chrome extension, or add
debug_mode: trueto your gtag config - Run your app locally and trigger the tour
- Watch for
tourkit_tour_started,tourkit_step_viewed, andtourkit_tour_completedin the event stream
If events don't appear, check these common causes:
gtag.jsisn't loaded before Tour Kit initializes (move the script higher in<head>)- Ad blockers intercept the GA4 beacon (test in an incognito window with extensions disabled)
- The Measurement ID is wrong (copy it directly from GA4 Admin > Data Streams)
We tested this in a Next.js 15 project with Tour Kit and gtag.js loaded via Google Tag Manager. Events appeared in DebugView within 2-3 seconds of each tour interaction.
Step 5: Build a tour completion funnel
GA4's Funnel Exploration turns your tour events into a visual drop-off analysis that shows exactly which step loses the most users. The feature supports up to 10 steps per funnel, which is plenty for typical onboarding flows of 3-7 steps. This is where the tracking setup pays off.
- Go to GA4 > Explore > Create new exploration > Funnel exploration
- Add steps in order:
- Step 1: Event =
tourkit_tour_started - Step 2: Event =
tourkit_step_viewed, parameterstep_index= 1 - Step 3: Event =
tourkit_step_viewed, parameterstep_index= 2 - Step 4: Event =
tourkit_tour_completed
- Step 1: Event =
- Toggle "Make open funnel" if your onboarding allows users to enter at different steps
Open funnels are GA4's underappreciated feature for product tours. Closed funnels require users to hit step 1 first, which makes sense for checkout flows but not for onboarding where 20-30% of users may re-enter mid-flow. Open funnels (GA4 Funnel Exploration docs) count users from wherever they join, giving you accurate completion rates even for non-linear onboarding.
Add a breakdown by tour_id if you're running multiple tours. This shows which onboarding flow has the worst drop-off without building separate funnels for each.
Step 6: Track custom metadata with tour events
GA4 supports up to 25 custom parameters per event and 50 custom dimensions per property. Tour Kit's default parameters consume 5-6 of those 25 parameter slots, leaving about 19 for your own metadata: user plan, signup source, A/B test variant, or anything else relevant to your analysis. The metadata field on TourProvider and individual steps passes arbitrary key-value pairs straight through to GA4.
// src/components/onboarding-tour.tsx
'use client'
import { TourProvider, TourStep } from '@tourkit/react'
const steps = [
{
id: 'welcome',
target: '#dashboard-header',
content: 'Welcome to your dashboard',
metadata: { section: 'header' },
},
{
id: 'sidebar-nav',
target: '#sidebar',
content: 'Navigate between your projects here',
metadata: { section: 'navigation' },
},
{
id: 'create-project',
target: '#create-btn',
content: 'Start by creating your first project',
metadata: { section: 'action', cta: true },
},
]
export function OnboardingTour() {
return (
<TourProvider
tourId="onboarding-v2"
steps={steps}
metadata={{
user_plan: 'free',
signup_source: 'organic',
variant: 'control',
}}
>
{/* Your tour step UI components */}
</TourProvider>
)
}Those metadata fields appear as GA4 event parameters. Watch out for two limits: GA4 silently truncates parameter values at 100 characters, and parameters with more than 500 unique values get bucketed into an (other) row in reports (Google GA4 docs). Pass strings and numbers only. GA4 flattens nested objects into [object Object].
To see metadata in reports, register the parameters as custom dimensions in GA4 Admin > Data Display > Custom definitions. Unregistered parameters still appear in DebugView and the Events table, but can't be used as funnel breakdowns or in Explorations.
GA4 limits to plan around
Before you ship to production, know the hard limits. GA4 enforces these per property, and hitting them silently degrades your data:
| Limit | Value | What happens when exceeded |
|---|---|---|
| Unique event names | 500 per property | New events silently dropped |
| Custom parameters per event | 25 | Extra parameters ignored |
| Custom dimensions | 50 per property | Can't register new ones |
| Custom metrics | 50 per property | Can't register new ones |
| Parameter value length | 100 characters | Silently truncated |
| High-cardinality threshold | 500 unique values | Bucketed into (other) row |
| Free tier event volume | 10M events/month | Sampling applied |
Best practice is to focus on 15-25 meaningful events aligned to business goals rather than tracking every possible interaction (GA4Hell). Tour Kit's 6 event names fit comfortably within that range.
Common issues and troubleshooting
"Events appear in DebugView but not in standard reports"
GA4 standard reports have a 24-48 hour processing delay. Explorations update faster, typically 4-8 hours. DebugView is real time. If events show in DebugView but vanish from reports after 48 hours, check your data retention settings in Admin > Data Settings > Data Retention. Free GA4 properties default to 2 months; GA4 360 supports up to 50 months.
"Some step events are missing from the funnel"
GA4 batches events and sends them in groups. If a user navigates away mid-tour, the last few events in the batch may not fire. This is a GA4 limitation, not a Tour Kit issue. For critical tour completion tracking, consider adding the PostHog plugin as a secondary analytics provider. Tour Kit's plugin system supports multiple simultaneous destinations.
"Parameter values show as (not set)"
You either haven't registered the parameter as a custom dimension, or the parameter name has a typo. GA4 is case-sensitive: tour_id and Tour_Id are two different parameters that consume 2 of your 50 custom dimension slots. Tour Kit uses snake_case consistently, so stick with that convention for metadata keys too.
"The 500 event name limit feels tight"
Use parameterized events, not unique event names. A single tourkit_step_viewed event with a step_id parameter scales to thousands of steps. If you created separate events like tour_step_welcome_viewed and tour_step_sidebar_viewed, you'd burn through the 500-name budget fast. Tour Kit's plugin handles this correctly by default: one event name per lifecycle action, with step details as parameters.
Next steps
You've got Tour Kit firing structured GA4 events and a funnel showing onboarding drop-off. Here's where to go from here:
- Build a GA4 audience of
tourkit_tour_completedusers and compare their 7-day and 30-day retention against non-completers - Set up a Mixpanel funnel alongside GA4 for real-time analysis (GA4's 24-48 hour processing delay limits rapid iteration)
- Use the
tour_abandonedevent with thestep_indexparameter to identify which specific step causes the most exits, then rewrite that step's content - Add the Tour Kit scheduling package to trigger tours based on GA4 session timing data
FAQ
Does GA4 track product tour events automatically?
GA4 does not include recommended events for product tours. You need custom events. Tour Kit's googleAnalyticsPlugin maps 6 lifecycle callbacks to GA4 custom events with structured parameters like tour_id, step_index, and duration_ms. No manual gtag() calls required.
How many GA4 custom events can I create for tours?
GA4 allows 500 unique event names per property and 25 custom parameters per event (Google GA4 docs). Tour Kit's plugin uses 6 event names, about 1% of your budget. Use parameterized events with step details as parameters rather than unique event names per step.
Does adding GA4 tracking affect Tour Kit's performance?
Tour Kit's GA4 plugin calls window.gtag(), which is already loaded on your page. The plugin adds no extra network requests or JavaScript bundles beyond GA4's existing gtag.js (about 28KB gzipped). Events fire asynchronously and don't block tour transitions. We measured under 0.3ms per event call in Chrome DevTools on a mid-range laptop.
How is this different from tracking tours with PostHog or Mixpanel?
GA4 is free for up to 10 million events per month and runs on 37.9 million websites (SQ Magazine, 2026). PostHog offers real-time funnels but caps free usage at 1M events/month. Mixpanel costs more at scale. Tour Kit's plugin architecture supports all three simultaneously.
Can I track accessibility interactions in the tour funnel?
You can track keyboard-only tour navigation and reduced-motion variants as custom GA4 event metadata. GA4 cannot detect screen reader usage, and Google has no plans to add this (BOIA, 2026). Track feature usage patterns, not disability status. That's the ethical and WCAG-aligned approach.
Related articles

Amplitude + Tour Kit: measuring onboarding impact on retention
Wire Tour Kit callbacks to Amplitude track() for onboarding funnels, behavioral cohorts, and retention analysis. TypeScript examples included.
Read article
How to add a product tour to an Astro site with React islands
Add interactive product tours to an Astro site using React islands. Covers client directives, Nanostores state sharing, and Tour Kit setup.
Read article
Building conditional product tours based on user role
Build role-based product tours in React with Tour Kit. Filter steps by admin, editor, or viewer roles using the when prop and React Context.
Read article
Using CSS container queries for responsive product tours
Build product tour tooltips that adapt to their container, not the viewport. Learn CSS container queries with Tour Kit for truly responsive onboarding.
Read article