Install StepsKit (Next.js)
Use next/script to load StepsKit and identify the user in a single
spot. This guide assumes the App Router; the Pages Router pattern is the
same — just put the <Script> in _app.tsx.
Add Script to your root layout
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://cdn.stepskit.com/stepskit.latest.js"
data-api-key="YOUR_API_KEY"
strategy="afterInteractive"
/>
</body>
</html>
);
}strategy="afterInteractive" defers the script until after hydration —
the right choice for non-critical scripts like StepsKit.
Identify the user
If your user data is available server-side (most apps), render it into
data-user-* attributes and you're done — no extra JavaScript needed:
// app/layout.tsx
const user = await getCurrentUser();
<Script
src="https://cdn.stepskit.com/stepskit.latest.js"
data-api-key="YOUR_API_KEY"
data-user-id={user?.id}
data-user-email={user?.email}
data-user-plan={user?.plan}
strategy="afterInteractive"
/>If the user logs in or changes plan client-side, call
setUserAttributes from wherever the change happens. Use
autoRefresh: true so tours re-evaluate against the new identity:
"use client";
function onPlanUpgrade(plan: string) {
window.stepskit?.setUserAttributes(
{ plan },
{ autoRefresh: true },
);
}A note on the init/refresh race
If the script loads before you can identify the user, StepsKit's initial
tour fetch goes out without a visitor_id. The embed coordinates
internally so that when you call setUserAttributes({ id }, { autoRefresh: true }),
the in-flight fetch is discarded and a fresh fetch with the correct
visitor_id replaces it. This matters for "show once" tours — see
Visitor identification.
The practical takeaway: identify the user as early as you can, but you don't need to delay rendering for it.