Visitor identification

Pass data-user-id (or id via setUserAttributes). It's the single most important thing you can do for StepsKit, because every "per-visitor" rule keys on it.

Why id matters

  • Frequency capping. "Show once per visitor" requires a stable visitor identifier — see Frequency capping.
  • Analytics. Per-user funnel views and tour-completion rates roll up by id.
  • Per-user targeting. Targeting a specific person (email = me@…) during testing needs an id to attribute the impression correctly.

Anonymous visitors

If you don't set an id, StepsKit treats the visitor as anonymous:

  • Targeting rules that require a specific user attribute will not match.
  • Rules that target all visitors (the default) still match.
  • "Show once" falls back to a session-scoped check using browser storage. It works within a session but resets across devices and after clearing site data.

This is a reasonable default for marketing sites or signed-out screens. It's the wrong default for an authenticated SaaS app — pass id as early as you can.

The init/refresh race condition

There's a subtle ordering issue worth knowing about when you identify the user client-side (e.g., from a Next.js <Script onLoad> or after a React auth callback):

  1. The StepsKit script loads.
  2. It fetches the active tours for your project — but without a visitor_id, because you haven't identified yet.
  3. Your onLoad (or auth callback) fires and calls setUserAttributes({ id, ... }, { autoRefresh: true }).
  4. The first fetch is still in flight.

If StepsKit didn't handle this, a "show once" tour could play between steps 2 and 3, even for a visitor who has already seen it.

It does handle it. When you call setUserAttributes with autoRefresh: true, the embed marks the first fetch as stale. When the stale fetch resolves, it's discarded — playback is suppressed. The fresh fetch (with the correct visitor_id) replaces it.

You don't need to do anything special. The only practical advice: always pass autoRefresh: true when identifying the user, so the embed knows the previous fetch is no longer authoritative.

Choosing an id

Use your internal user ID — whatever value uniquely identifies a user in your app. It can be a UUID, an integer, a Stripe customer ID, anything that's stable per user across sessions. Don't use email (changes), and don't use session IDs (rotate too often).