Skip to main content

Google Analytics 4 + Tour Kit: event tracking for onboarding

Wire Tour Kit's analytics plugin to GA4 custom events for tour funnels, step drop-off, and onboarding completion tracking. TypeScript examples.

DomiDex
DomiDexCreator of Tour Kit
April 7, 202610 min read
Share
Google Analytics 4 + Tour Kit: event tracking for onboarding

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/analytics

What 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.js loaded 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/analytics

The 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 eventGA4 event nameParameters sent
tour_startedtourkit_tour_startedtour_id, total_steps
step_viewedtourkit_step_viewedtour_id, step_id, step_index, total_steps
step_completedtourkit_step_completedtour_id, step_id, step_index, duration_ms
tour_completedtourkit_tour_completedtour_id, total_steps, duration_ms
tour_skippedtourkit_tour_skippedtour_id, step_index, total_steps
tour_abandonedtourkit_tour_abandonedtour_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.

  1. Open GA4 Admin > Data Display > DebugView
  2. Install the Google Analytics Debugger Chrome extension, or add debug_mode: true to your gtag config
  3. Run your app locally and trigger the tour
  4. Watch for tourkit_tour_started, tourkit_step_viewed, and tourkit_tour_completed in the event stream

If events don't appear, check these common causes:

  • gtag.js isn'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.

  1. Go to GA4 > Explore > Create new exploration > Funnel exploration
  2. Add steps in order:
    • Step 1: Event = tourkit_tour_started
    • Step 2: Event = tourkit_step_viewed, parameter step_index = 1
    • Step 3: Event = tourkit_step_viewed, parameter step_index = 2
    • Step 4: Event = tourkit_tour_completed
  3. 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:

LimitValueWhat happens when exceeded
Unique event names500 per propertyNew events silently dropped
Custom parameters per event25Extra parameters ignored
Custom dimensions50 per propertyCan't register new ones
Custom metrics50 per propertyCan't register new ones
Parameter value length100 charactersSilently truncated
High-cardinality threshold500 unique valuesBucketed into (other) row
Free tier event volume10M events/monthSampling 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_completed users 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_abandoned event with the step_index parameter 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.


Ready to try userTourKit?

$ pnpm add @tour-kit/react