Skip to main content
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.

Metadata

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.
ConcernSafe?Notes
Static import at top of fileYesModule is SSR-safe
Calling initialize() in useEffectYesRuns browser-only
Dynamic import('@userplane/sdk') in useEffectYesBundle-size optimization only
Calling initialize() in a loaderNoLoaders run on the server
Missing search params in route schemaNoRouter throws notFoundError

Example app

A complete TanStack Start example is available at github.com/wizenheimer/userplane-sdk-examples/tree/main/examples/tanstack-start.
VariableDescription
VITE_USERPLANE_WORKSPACE_IDYour Userplane workspace ID
cd examples/tanstack-start && npm install && npm run dev