
Tour Kit + Segment: piping tour events to every analytics tool
Most product tour libraries send events to one analytics tool. You wire up GA4 or Mixpanel, declare victory, and move on. Six months later, the data team asks for the same events in BigQuery. Then marketing wants them in HubSpot. Then someone adds Amplitude. You're writing glue code for each destination, and the tour config file you swore was clean now has four analytics callbacks.
Segment solves this by sitting between your app and every downstream tool. One analytics.track() call fans out to 400+ destinations without extra code. As of April 2026, @segment/analytics-node pulls roughly 539K weekly downloads on npm, and Segment's Analytics.js 2.0 ships at about 16KB gzipped after a 70% reduction from the v1 bundle (Segment Engineering Blog).
Tour Kit's plugin-based analytics architecture maps cleanly to Segment's track and identify methods. You write one plugin, and every tour event in your app flows to Amplitude, Mixpanel, BigQuery, HubSpot, and wherever else your Segment workspace is configured to send data.
npm install @tourkit/core @tourkit/react @tourkit/analyticsWhat you'll build
You'll create a custom Tour Kit analytics plugin that sends structured tour lifecycle events through Segment to every connected destination, mapping 6 core event types to Segment's track API with typed properties and user identification support. The plugin works with both Analytics.js 2.0 in the browser and @segment/analytics-node on the server. About 50 lines of TypeScript across 2 files.
Tour Kit requires React 18.2+ and has no visual tour builder. You write steps in code. If your team needs drag-and-drop, Chameleon or Appcues are better fits for that workflow.
Why Segment + Tour Kit?
Segment is a customer data platform (CDP) that acts as a single collection point for all your event data and routes it to downstream tools without per-destination integration code. Tour Kit's @tour-kit/analytics package already ships plugins for GA4, PostHog, Mixpanel, and Amplitude. But each of those is a point-to-point connection. Segment replaces all four with one pipe.
The practical benefit: when you add a new analytics tool next quarter, you configure it in the Segment dashboard. Zero code changes in your React app.
| Approach | Tools supported | Code changes per new tool | Tour Kit setup |
|---|---|---|---|
| Direct plugins (GA4, Mixpanel, etc.) | 1 per plugin | New plugin + provider config | ~8 lines per tool |
| Segment plugin | 400+ via Segment dashboard | Zero (dashboard toggle) | ~12 lines, once |
The tradeoff is cost. Segment's free tier caps at 1,000 monthly tracked users (MTUs) with 2 sources (Segment Pricing). That covers early-stage apps, but Segment's pricing jumped roughly 40% in 2025, so growing teams hit a wall fast. Tour Kit's plugin architecture hedges this: if you outgrow Segment, swap the plugin for RudderStack or a direct integration without touching your tour code.
Prerequisites
Before starting, make sure you have these tools and accounts set up, since the integration touches both your React app and the Segment dashboard where destinations are configured.
- React 18.2+ or React 19
- A Segment workspace with a JavaScript source (grab the write key from Sources > JavaScript > Settings)
- Analytics.js 2.0 loaded on your page (via Segment's snippet or
@segment/analytics-next) - Tour Kit installed:
@tourkit/core,@tourkit/react,@tourkit/analytics - At least one Segment destination enabled (GA4, Amplitude, a warehouse, anything)
If you don't have a tour yet, the React 19 quickstart gets you running in 5 minutes.
Step 1: Load Segment's Analytics.js
Analytics.js 2.0 is Segment's browser-side SDK that collects events and routes them to downstream destinations, and it's the piece your Tour Kit plugin will call through window.analytics. Segment recommends loading it via their snippet in your HTML <head>. The initial bundle is about 16KB gzipped, with destination-specific code loaded on demand (Segment Docs). If you're using a bundler and prefer npm, @segment/analytics-next works too, but the snippet approach keeps Segment out of your JS bundle entirely.
For Next.js, add the snippet to your root layout:
// src/app/layout.tsx
import Script from 'next/script'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Script
id="segment-snippet"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `!function(){var i="analytics",...}()` // Paste your Segment snippet here
}}
/>
</head>
<body>{children}</body>
</html>
)
}Replace the snippet placeholder with the actual code from your Segment workspace under Sources > JavaScript > Quickstart. The write key is embedded in the snippet.
Step 2: Build the Segment plugin
Tour Kit's AnalyticsPlugin interface has 5 methods (init, track, identify, flush, and destroy) that map directly to Segment's analytics.track() and analytics.identify() calls, making a custom plugin about 40 lines of TypeScript with full type safety and an optional event name mapping for teams with existing naming conventions.
// src/lib/segment-plugin.ts
import type { AnalyticsPlugin, TourEvent } from '@tour-kit/analytics'
declare global {
interface Window {
analytics?: {
track: (event: string, properties?: Record<string, unknown>) => void
identify: (userId: string, traits?: Record<string, unknown>) => void
flush?: () => void
}
}
}
interface SegmentPluginOptions {
/** Event name prefix (default: 'tourkit_') */
eventPrefix?: string
/** Map Tour Kit event names to custom Segment event names */
eventNameMap?: Partial<Record<string, string>>
}
export function segmentPlugin(options: SegmentPluginOptions = {}): AnalyticsPlugin {
const prefix = options.eventPrefix ?? 'tourkit_'
const getAnalytics = () => {
if (typeof window !== 'undefined' && window.analytics) {
return window.analytics
}
return null
}
const resolveEventName = (eventName: string): string => {
if (options.eventNameMap?.[eventName]) {
return options.eventNameMap[eventName]
}
return `${prefix}${eventName}`
}
return {
name: 'segment',
init() {
if (!getAnalytics()) {
console.warn(
'Tour Kit Segment plugin: window.analytics not found. ' +
'Make sure the Segment snippet is loaded before Tour Kit initializes.'
)
}
},
track(event: TourEvent) {
const seg = getAnalytics()
if (!seg) return
seg.track(resolveEventName(event.eventName), {
tour_id: event.tourId,
step_id: event.stepId,
step_index: event.stepIndex,
total_steps: event.totalSteps,
duration_ms: event.duration,
...event.metadata,
})
},
identify(userId: string, properties?: Record<string, unknown>) {
const seg = getAnalytics()
if (!seg) return
seg.identify(userId, properties)
},
flush() {
const seg = getAnalytics()
seg?.flush?.()
},
destroy() {
// Segment manages its own lifecycle
},
}
}The eventNameMap option lets you override specific event names if your data team has an existing naming convention. By default, events arrive in Segment as tourkit_tour_started, tourkit_step_viewed, and so on. Segment then forwards those exact names to every destination.
Step 3: Wire the plugin into your app
Connecting the Segment plugin to your React app follows the same provider pattern as Tour Kit's other analytics integrations: create an analytics instance with createAnalytics, wrap your component tree with AnalyticsProvider, and every tour in the tree starts sending events automatically. If you've done the GA4 integration, this will look familiar.
// src/lib/analytics.ts
import { createAnalytics } from '@tour-kit/analytics'
import { segmentPlugin } from './segment-plugin'
export const analytics = createAnalytics({
plugins: [
segmentPlugin({
eventPrefix: 'tourkit_',
}),
],
debug: process.env.NODE_ENV === 'development',
})Then wrap your layout:
// 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>
)
}Every tour in your app now sends events to Segment. No per-tour configuration needed.
Want Segment and a direct GA4 connection? Stack plugins:
import { googleAnalyticsPlugin } from '@tour-kit/analytics/google-analytics'
export const analytics = createAnalytics({
plugins: [
segmentPlugin({ eventPrefix: 'tourkit_' }),
googleAnalyticsPlugin({ measurementId: 'G-XXXXXXXXXX' }),
],
})Both fire in parallel on every tour event.
Step 4: Verify events in Segment Debugger
Segment's Debugger (Sources > your source > Debugger) shows events in real time as they arrive from your browser, making it the fastest way to confirm the integration works before events propagate to downstream destinations like GA4 or Amplitude. Trigger a tour in your dev environment and watch for tourkit_tour_started with the expected properties.
If events don't appear, check these common causes:
- The Segment snippet loads before Tour Kit initializes. In Next.js,
strategy="afterInteractive"on the Script tag handles this. - Ad blockers intercept Segment's
api.segment.ioendpoint. Test in incognito with extensions disabled. - The write key in the snippet matches your source. Copy-paste errors are the number one cause of silent failures.
We tested this in a Next.js 15 app with Analytics.js 2.0. Events appeared in the Debugger within 1-2 seconds. Downstream destinations received events within their usual windows: real-time for Amplitude, 4-8 hours for GA4 Explorations.
Here's what the event payload looks like in Segment's Debugger:
| Tour Kit event | Segment event name | Properties 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 |
Segment forwards these property names as-is to most destinations. Some tools (GA4, Amplitude) have parameter naming conventions, so check your destination settings if properties appear renamed downstream.
Going further
Once events flow through Segment, you can build on the pipeline without touching tour code:
- Segment Protocols validates your event schema before data reaches destinations. Define a tracking plan with the 6 Tour Kit event types and their required properties. Events that don't match get flagged or blocked.
- Segment Journeys lets you define multi-step user paths: "User started tour > completed tour > used feature within 7 days." These journey definitions power targeted email campaigns, in-app messages, and cohort analysis in downstream tools.
- Warehouse sync dumps raw events to BigQuery, Snowflake, or Redshift on a schedule. Your data team gets the full event stream for custom SQL analysis without requesting new instrumentation.
- Swap CDPs later if Segment's pricing doesn't scale. Tour Kit's plugin interface works identically with RudderStack (open-source, warehouse-native) or Hightouch. Change the plugin, keep the tour code.
The rise of composable CDPs means the "single pipe" pattern is becoming standard (Dinmo, 2026). Tour Kit's plugin architecture was designed for exactly this: instrument once, route everywhere.
FAQ
Does Segment have built-in product tour event tracking?
Segment does not define a standard event schema for product tours. Their Spec covers e-commerce, video, and mobile events, but onboarding flows require custom track calls. Tour Kit's Segment plugin fills this gap with 6 typed lifecycle events, giving you a consistent schema without designing the taxonomy yourself.
How much does Segment cost for tour event tracking?
Segment's free tier includes 1,000 monthly tracked users (MTUs), 2 sources, and unlimited destinations (Segment Pricing, April 2026). Tour events don't increase your MTU count beyond what your app already tracks. Paid tiers require a sales quote. Budget-conscious teams should note that Segment pricing increased roughly 40% in 2025.
Does adding Segment increase my page load time?
Analytics.js 2.0 loads about 16KB gzipped initially, with destination SDKs loaded on demand (Segment Engineering Blog). Combined with Tour Kit's core at under 8KB gzipped, total overhead is roughly 24KB. Events fire asynchronously and don't block tour transitions. We measured no perceptible impact on Interaction to Next Paint (INP) in our test app.
Can I use Tour Kit's Segment plugin with server-side rendering?
The plugin checks for window.analytics before calling any method, so it's SSR-safe. Events only fire in the browser after hydration. For server-side event tracking (e.g., recording tour completions from an API route), use @segment/analytics-node directly instead of the browser plugin.
What if I outgrow Segment's free tier?
Tour Kit's analytics plugin interface is CDP-agnostic. Write a RudderStack plugin with the same track and identify methods, swap it into createAnalytics, and your tour code stays identical. RudderStack is open-source and warehouse-native, making it the most cost-effective Segment alternative as of 2026 (GenesysGrowth).
Related articles

Tour Kit + Intercom: show tours before chat, not after
Integrate Tour Kit with Intercom to show contextual product tours before users open chat. Working code, event bridging, and the gotchas we hit.
Read article
Tour Kit + Storybook: documenting tour components in isolation
Build and test product tour components in Storybook with Autodocs, play functions, and the a11y addon. Working TypeScript examples included.
Read article
Tour Kit + Supabase: tracking tour state per user
Persist product tour progress in Supabase PostgreSQL with Row Level Security. Replace localStorage with cross-device tour state in under 100 lines.
Read article
Tour Kit + TanStack Router: multi-page tours with type safety
Build type-safe multi-page product tours with Tour Kit and TanStack Router. Route context, beforeLoad guards, and typed search params for onboarding flows.
Read article