Install on Next.js and React
Use Next.js's Script component in the root layout for App Router, or _document.tsx for Pages Router.
Draft — for review.
On Next.js, the right place for the Seena widget is the root layout (App Router) or _document.tsx (Pages Router) — whichever renders into every page's <head>. Using Next's built-in <Script> component gives you better loading behavior than a raw <script> tag.
Next.js App Router
Open your root layout (usually src/app/layout.tsx) and add the Script import alongside your existing imports:
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://app.seenalabs.io/widget/seena.js"
data-site-id="YOUR_SITE_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}
Why strategy="afterInteractive"? It loads the widget after your page becomes interactive — fast enough to capture sessions, late enough that it doesn't compete with your critical render path. This is the same strategy the Seena Labs marketing site uses on itself.
Next.js Pages Router
Open pages/_document.tsx (or create one if you don't have it) and add the script in the Head:
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head>
<script
src="https://app.seenalabs.io/widget/seena.js"
data-site-id="YOUR_SITE_ID"
async
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
_document.tsx only renders once per full page load, which matches what we want for the widget.
Plain React (Create React App, Vite, etc.)
If you're not on Next.js, just add the script tag to public/index.html (CRA) or index.html (Vite) in the <head>, the same way you would for a plain HTML site:
<script
src="https://app.seenalabs.io/widget/seena.js"
data-site-id="YOUR_SITE_ID"
async
></script>
Don't load it from inside a component — if React re-mounts the component, the widget will try to initialize twice and log a warning.
TypeScript — avoiding the data-site-id lint warning
TypeScript's React types don't type data-site-id as a known attribute; most linters are fine with it, but if yours complains, you can cast:
<Script
src="https://app.seenalabs.io/widget/seena.js"
{...({ 'data-site-id': 'YOUR_SITE_ID' } as any)}
strategy="afterInteractive"
/>
Or use the lowercase-hyphen form directly, which is standard HTML.
What to do next
- Quickstart — confirm everything is wired up.
- How Interview Agents work — including how to pass
userand session metadata.