Install

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