Next.js
Install Sift in the App Router or Pages Router without blocking hydration.
Use next/script in the root layout. It loads after hydration, so the script
never competes with your app's JavaScript.
App Router
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Script
defer
data-domain="example.com"
src="https://sift.agentonboard.xyz/js/sift.js"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
);
}afterInteractive is the right strategy here. beforeInteractive delays your
app for a metric, which is the opposite of what an analytics script should do.
Pages Router
import { Head, Html, Main, NextScript } from "next/document";
import Script from "next/script";
export default function Document() {
return (
<Html lang="en">
<Head>
<Script
defer
data-domain="example.com"
src="https://sift.agentonboard.xyz/js/sift.js"
strategy="afterInteractive"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}Client-side navigation
The App Router changes the URL with history.pushState. Sift listens for that,
so route changes are counted without a reload and without extra code.
One caveat: Sift dedupes identical URLs, not screens. If a transition writes two different URLs for the same screen, both are counted. See SPA routes.
Verify it
Run the app on a real domain, or a preview deployment. localhost is ignored by
design. Then open the site's install page in Sift and watch for Signal
detected.
Content Security Policy
If you set a CSP, allow the script and the beacon endpoint:
script-src https://sift.agentonboard.xyz;
connect-src https://sift.agentonboard.xyz;