Skip to main content

Next.js

Install the Selgeo tracking snippet in a Next.js application using the framework's first-party next/script component. This guide covers both the App Router (app/layout.tsx) and the Pages Router (pages/_app.tsx).

API Version: v1

If you are not on Next.js, see the HTML / plain-script guide, the React (Vite) guide, or the WordPress guide instead.

Why next/script?

A bare <script> tag in a React component does not behave the way it does in plain HTML — Next.js bundles JavaScript per-route and hydrates pages incrementally. The official next/script component:

  • Loads the snippet exactly once across client-side navigations (no duplicate execution).
  • Honours a load strategy you control (we use afterInteractive).
  • Plays nicely with Next.js's streaming SSR and Partial Prerendering.

We use strategy="afterInteractive" rather than lazyOnload so the snippet is ready before the first ?ref= parameter is processed. Attribution accuracy beats a marginal Lighthouse score gain — lazyOnload risks missing an early click that converts immediately.

Installation — App Router (app/layout.tsx)

For projects on Next.js 13+ using the app/ directory.

Step 1: Add the snippet to the root layout

Open app/layout.tsx and add the Script import and JSX element:

import Script from 'next/script';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<Script
async
src="https://cdn.selgeo.com/v1/selgeo.js"
data-merchant="pk_test_YOUR_KEY"
strategy="afterInteractive"
/>
</body>
</html>
);
}

Replace pk_test_YOUR_KEY with your public API key from Settings > API Keys in the Selgeo dashboard.

Settings &gt; API Keys page in the Selgeo dashboard with the public key field highlighted

Step 2: Save and run

pnpm dev
# or
npm run dev

Open your site in the browser. The snippet now loads on every page rendered by the App Router.

Installation — Pages Router (pages/_app.tsx)

For projects on the legacy pages/ directory.

Step 1: Add the snippet to the custom App component

Open (or create) pages/_app.tsx:

import type { AppProps } from 'next/app';
import Script from 'next/script';

export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
async
src="https://cdn.selgeo.com/v1/selgeo.js"
data-merchant="pk_test_YOUR_KEY"
strategy="afterInteractive"
/>
</>
);
}

Step 2: Restart the dev server

pnpm dev

Pages-Router projects must restart the dev server when _app.tsx is created for the first time. Subsequent edits hot-reload normally.

The Tracking Snippet card in Settings showing the Next.js tab with the import + component blocks ready to copy

Required and optional attributes

AttributeRequiredDescription
srcYesCDN URL. Always https://cdn.selgeo.com/v1/selgeo.js.
data-merchantYesYour public API key (pk_test_* or pk_live_*).
asyncYesAsynchronous loading; does not block hydration.
strategyYesUse "afterInteractive". Do not switch to "lazyOnload" — early referral clicks may be missed.
data-debugNoEnables verbose console logging. Remove before going live.
data-api-urlNoOverrides the API endpoint. Only present for staging / development workspaces — the Selgeo dashboard injects it automatically when needed.

Verifying your installation

  1. Build and run your project (pnpm dev or pnpm build && pnpm start).
  2. Create a tracking link in the Selgeo dashboard under Programs > Tracking Links.
  3. Visit your site with the tracking link, for example:
    https://your-site.com/?ref=YOUR_TEST_REF
  4. Open Developer Tools (F12) and check the Console. With data-debug added temporarily, you should see:
    [selgeo] ref detected YOUR_TEST_REF
    [selgeo] click_id stored xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  5. In the browser console, run:
    __selgeo.getClickId()
    This should return a UUID string. null means the snippet did not detect a ?ref= parameter.
  6. Open the Selgeo dashboard. The click should appear under Analytics within a few seconds.

Selgeo Analytics view showing recent click activity and top partners

Troubleshooting

Snippet not loaded

  • Confirm app/layout.tsx (App Router) or pages/_app.tsx (Pages Router) actually contains the <Script> block — accidental duplication into a single page file is the most common mistake.
  • Verify the src is exactly https://cdn.selgeo.com/v1/selgeo.js. A typo silently fails the script load.
  • Open the Network tab and filter by selgeo.js. A red entry with status 0 or a CORS error usually indicates a CSP violation — see below.
  • If you are running behind a static export (output: 'export'), confirm the snippet is included in the exported HTML. next/script with strategy="afterInteractive" is preserved by static export.

Click not tracked

  • The visitor must arrive with ?ref=… on the initial page load. Once the snippet captures the click, the URL is rewritten and the parameter no longer appears. Reload the page with a fresh tracking link if you have already consumed the current one.
  • Confirm data-merchant contains a valid pk_test_* or pk_live_* key. A truncated or whitespace-prefixed value will silently no-op.
  • If you use React Strict Mode, ensure the snippet is in app/layout.tsx or pages/_app.tsx, not inside an effect that may run twice. next/script already deduplicates correctly.
  • Check the Selgeo dashboard mode (test vs live). A pk_live_* key on a tracking link issued in test mode will not register the click.

CSP blocking

If your Next.js project sets a Content-Security-Policy header (next.config.js headers(), middleware, or a Vercel vercel.json entry), allow the Selgeo origins:

script-src 'self' https://cdn.selgeo.com;
connect-src 'self' https://api.selgeo.com;

For staging / dev workspaces where data-api-url points elsewhere, add that origin to connect-src as well. If you cannot relax CSP, consider routing the snippet through your own origin (advanced, out of scope for this guide).

Next steps