Skip to main content

Installing

Install on Next.js

Both routers, plus the proxy setup that makes Next.js the easiest platform to run TrueStat first-party.

Both routers, plus the proxy setup that makes Next.js the easiest platform to run TrueStat first-party.

App Router

Add the tag to your root layout using next/script.

app/layout.tsx:

import Script from "next/script";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://truestat.io/js/t.js"
          data-website-id="ts_a1b2c3d4e5f6"
          data-domain="example.com"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Use strategy="afterInteractive". It is the right one for a tracker: the script loads after hydration, does not block anything, and still catches the first page view. beforeInteractive would load it earlier at a cost to your Largest Contentful Paint for no gain, and lazyOnload can miss short visits.

Because the tag is in the root layout, it applies to every route including nested layouts and route groups. You do not need to repeat it.

screenshot coming

The install page with the "Next.js" platform tab selected, showing the App Router variant with the real site key. Light theme.

Pages Router

pages/_document.tsx:

import { Html, Head, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <script
          defer
          data-website-id="ts_a1b2c3d4e5f6"
          data-domain="example.com"
          src="https://truestat.io/js/t.js"
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

_document.tsx renders once per page, so this covers every route.

Client-side navigation is handled

You do not need to do anything for route changes. The script watches history.pushState, history.replaceState and popstate, and records a view on each path change.

Navigations are debounced at 100 ms. Next.js's router frequently calls replaceState immediately after pushState, and without the debounce every navigation would count as two page views.

If your app uses hash-based routing (/#/dashboard), path changes in the hash are not tracked by default. Add data-ts-track-hash="true". See Single-page apps.

Keeping your own environment variables out of the tag

Nothing in the tag is secret, so there is no security reason to hide the site key. But if you prefer to keep it in your environment:

<Script
  src="https://truestat.io/js/t.js"
  data-website-id={process.env.NEXT_PUBLIC_TRUESTAT_SITE_ID}
  data-domain={process.env.NEXT_PUBLIC_TRUESTAT_DOMAIN}
  strategy="afterInteractive"
/>

It must be a NEXT_PUBLIC_ variable — the value has to reach the browser.

Serving from your own domain

Next.js is the easiest platform for this, because it is config only. Add rewrites to next.config.ts:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      { source: "/js/t.js", destination: "https://truestat.io/js/t.js" },
      { source: "/api/events", destination: "https://truestat.io/api/events" },
    ];
  },
};

export default nextConfig;

Then change the tag's src to the relative path:

<Script
  src="/js/t.js"
  data-website-id="ts_a1b2c3d4e5f6"
  data-domain="example.com"
  strategy="afterInteractive"
/>

You do not need data-api-url. When the script's own src is not on truestat.io, it sends events to /api/events on the page's own origin automatically.

Next.js rewrites forward the visitor's IP correctly with no extra configuration. That matters more than it sounds — see Serving the script from your own domain, which explains why a proxy that drops the client IP collapses all of your visitors into one.

Content Security Policy

If you set a CSP in next.config.ts under headers(), both directives are needed:

script-src 'self' https://truestat.io; connect-src 'self' https://truestat.io;

If you are using the rewrites above, 'self' covers both and you need no TrueStat entry at all.

Verify

Load a page, then check the install page in the app — it flips to Connected within seconds. Then click a link to a second route and confirm the view count goes up by one, not two.

Was this page helpful?

Last updated August 28, 2026

Install on Next.js | TrueStat | NookDocs