This guide covers how to install Userplane in a TanStack Start application.
Adding the script
The fastest way to add Userplane is the CDN embed. Add these two tags to the <head> in your root route’s <Head> component:
// app/routes/__root.tsx
import { createRootRoute, HeadContent, Outlet } from '@tanstack/react-router';
export const Route = createRootRoute({
head: () => ({
meta: [{ name: 'userplane:workspace', content: 'YOUR_WORKSPACE_ID' }],
scripts: [{ type: 'module', src: 'https://cdn.userplane.io/embed/script.js' }],
}),
component: RootComponent,
});
function RootComponent() {
return (
<>
<HeadContent />
<Outlet />
</>
);
}
You can copy the snippet with your workspace ID pre-filled from Workspace Settings > Domains in the Userplane dashboard.
npm SDK
Use the npm SDK when you need programmatic control — triggering recordings from a button, attaching user metadata, or reading recording state.
Installation
npm install @userplane/sdk
Initialization
Create a UserplaneProvider component and render it inside your root route component. Use useEffect with a dynamic import() to ensure initialization runs only in the browser.
// app/components/UserplaneProvider.tsx
'use client';
import { useEffect } from 'react';
export function UserplaneProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
import('@userplane/sdk').then(({ initialize }) => {
initialize({
workspaceId: import.meta.env.VITE_USERPLANE_WORKSPACE_ID,
});
});
}, []);
return <>{children}</>;
}
// app/routes/__root.tsx
import { UserplaneProvider } from '../components/UserplaneProvider';
function RootComponent() {
return (
<UserplaneProvider>
<HeadContent />
<Outlet />
</UserplaneProvider>
);
}
URL parameters
Important: TanStack Router validates search parameters against a Zod schema. If a route schema
does not include userplane-token, userplane-action, and userplane-workspace, the router
throws a notFoundError when Userplane appends those params to the URL.
Add the Userplane params to your root route’s search param schema:
// app/routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router';
import { z } from 'zod';
const searchSchema = z.object({
'userplane-token': z.string().optional(),
'userplane-action': z.string().optional(),
'userplane-workspace': z.string().optional(),
});
export const Route = createRootRoute({
validateSearch: searchSchema,
component: RootComponent,
});
Defining this on the root route means all child routes inherit it automatically. You can also use z.record(z.string()) for a more permissive catch-all if your app accepts arbitrary query parameters.
If your app redirects unauthenticated users to a login page, preserve the userplane- prefixed parameters through the redirect:
// Example: auth-protected route
import { redirect } from '@tanstack/react-router';
export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, search }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/login',
search: {
'userplane-token': search['userplane-token'],
'userplane-action': search['userplane-action'],
'userplane-workspace': search['userplane-workspace'],
},
});
}
},
});
See Installation for the full list of parameters.
Sensitive data
Add data-userplane-blur to any element you want blurred in recordings. See Sensitive Data Redaction for the full reference.
Call set() inside the useEffect after initialize():
useEffect(() => {
import('@userplane/sdk').then(({ initialize, set }) => {
initialize({ workspaceId: import.meta.env.VITE_USERPLANE_WORKSPACE_ID });
set('environment', import.meta.env.MODE);
});
}, []);
See Metadata SDK for the full API.
SSR
TanStack Start renders on the server before hydrating in the browser. @userplane/sdk is SSR-safe to import — it does not reference window or document at module evaluation time. The initialize() call must run client-side, which useEffect guarantees.
| Concern | Safe? | Notes |
|---|
Static import at top of file | Yes | Module is SSR-safe |
Calling initialize() in useEffect | Yes | Runs browser-only |
Dynamic import('@userplane/sdk') in useEffect | Yes | Bundle-size optimization only |
Calling initialize() in a loader | No | Loaders run on the server |
| Missing search params in route schema | No | Router throws notFoundError |
Example app
A complete TanStack Start example is available at github.com/wizenheimer/userplane-sdk-examples/tree/main/examples/tanstack-start.
| Variable | Description |
|---|
VITE_USERPLANE_WORKSPACE_ID | Your Userplane workspace ID |
cd examples/tanstack-start && npm install && npm run dev
Related articles