Framework guides
Two ways in for every stack: the script tag (nothing to install, updates itself) or the npm package (bundled with your app, you decide when it starts). Both record the same thing.
True for every framework
• Client-side navigation is recorded automatically — the SDK follows the History API, so there is no router integration to add.
• Server rendering is safe — the script tag only runs in the browser, and startRecording() does nothing on the server.
• Start early, once. The recorder belongs to the page, not a component: start it before your app renders (or with SpectoProvider at the root) and it captures the first requests and errors of every page load. Starting it again while it runs is harmless.
• Development is handled. Sessions from localhost and other development hosts are tagged dev, don't count toward your plan (up to a monthly allowance) and step aside once real traffic arrives. If your project lists allowed origins, add the ones you test on — the Install page offers to when it refuses one.
Next.js
App Router — script tag
In app/layout.tsx, with next/script (it passes data-* attributes through):
import Script from 'next/script' export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Script src="https://specto.sh/specto.js" data-key="pk_your_key" strategy="afterInteractive" /> </body> </html> ) }
App Router — npm package
A client provider, wrapped around the root layout's children:
'use client' // app/providers.tsx import { SpectoProvider } from '@spectosh/browser/react' export function Providers({ children }) { return <SpectoProvider apiKey={process.env.NEXT_PUBLIC_SPECTO_KEY}>{children}</SpectoProvider> } // app/layout.tsx: <body><Providers>{children}</Providers></body>
It records in the browser only (server rendering just renders the children), starts before your components' effects run, and follows route changes on its own. NEXT_PUBLIC_SPECTO_KEY=pk_your_key.
Pages Router
Put the same <Script … strategy="afterInteractive" /> in pages/_app.tsx, or wrap your custom App's page in SpectoProvider.
Remix
In app/root.tsx, next to <Meta /> and <Links />:
export default function App() { return ( <html lang="en"> <head> <Meta /> <Links /> <script src="https://specto.sh/specto.js" data-key="pk_your_key" defer /> </head> <body> <Outlet /> <ScrollRestoration /> <Scripts /> </body> </html> ) }
With the npm package, wrap <Outlet /> in <SpectoProvider apiKey="pk_your_key"> (from @spectosh/browser/react) in the root component.
Nuxt
Script tag, in nuxt.config.ts:
export default defineNuxtConfig({ app: { head: { script: [{ src: 'https://specto.sh/specto.js', 'data-key': 'pk_your_key', defer: true }], }, }, })
Or the npm package in a client-only plugin, plugins/specto.client.ts:
import { startRecording } from '@spectosh/browser' export default defineNuxtPlugin(() => { startRecording({ key: useRuntimeConfig().public.spectoKey }) // pk_your_key })
SvelteKit
Script tag, in src/app.html before %sveltekit.head%:
<script src="https://specto.sh/specto.js" data-key="pk_your_key" defer></script> %sveltekit.head%
Or the npm package, started in src/hooks.client.js — it runs in the browser before the app starts:
import { startRecording } from '@spectosh/browser' startRecording({ key: 'pk_your_key' })
Identify signed-in users
Once you know who is signed in, attach their id so you can find their sessions (Sessions → search by user id or trait):
// script tag: the call is skipped if the snippet hasn't loaded yet window.Specto?.identify(user.id, { plan: user.plan }) // npm package import { identify } from '@spectosh/browser' identify(user.id, { plan: user.plan }) // React: pass the user to the provider — identified whenever it changes <SpectoProvider apiKey="pk_your_key" user={{ id: user.id, plan: user.plan }}>
Prefer an opaque id to an email: traits are visible to everyone in your workspace. Everything else you can call is in the SDK reference; consent banners are covered in Consent banners.

