# How to add analytics to a Next.js site, App Router and Pages Router

> Where to put an analytics script in a Next.js app, how client-side navigation affects pageviews, CSP rules, and sending conversions from route handlers.

Source: https://datacenter.so/track/nextjs
By the Datacenter team. Updated October 23, 2026.

A Next.js site is a single-page app after the first load: links swap the page in the browser without a full reload. An analytics script must count those page changes, load without blocking rendering, and fit your Content Security Policy. Server-side conversions then come from your own route handlers.

If you deploy on Vercel, Vercel's Web Analytics is one option for page traffic. The steps below work on any host.

## Install Datacenter on Next.js

1. In Datacenter, add your site's address and copy the tag from **Set up your site**.
2. Open the root layout: `app/layout.tsx` with the App Router, or `pages/_document.tsx` with the Pages Router.
3. Add the tag inside `<head>`, as shown below.
4. Deploy the change.
5. Click **Check my site** in Datacenter, then open the deployed site.

With the App Router:

```tsx
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <script defer data-site="yoursite.com" src="https://datacenter.so/s.js"></script>
      </head>
      <body>{children}</body>
    </html>
  );
}
```

`next/script` with `strategy="afterInteractive"` also works. Keep the `data-site` attribute on it, since the script reads it to know which site it is on.

## Client-side navigation

Next.js changes pages with the browser's History API. Datacenter's script listens for those changes and counts each new page once, so you do not need a router event handler. If you add a second analytics tool, check that it does the same, or you will count only the first page of every visit.

## Content Security Policy

If your app sends a CSP header, allow the script and the endpoint it sends to:

```text
script-src 'self' https://datacenter.so;
connect-src 'self' https://datacenter.so;
```

With a nonce-based policy, pass the nonce to the script tag the same way you do for your other scripts. A blocked script fails silently, so check the browser console after deploying.

## Conversions from the server

Signups, trials and payments usually happen in your API routes, not in a page. Send them from there with the [Events API](https://datacenter.so/docs/events-api) on paid plans:

- one request per conversion, with the event name, the page URL and, for sales, the value, currency and order id,
- the visitor's first-party id from the `_dc_id` cookie with 90-day tracking, so the conversion joins the visits that led to it,
- the order id, so a browser event and a server event for the same order count once.

Keep the site's secret key in a server environment variable, never in client code.

## First-party serving

Some ad blockers block known analytics domains. The [script proxy](https://datacenter.so/docs/script-proxy) serves the script and its endpoint from your own domain on paid plans. It is optional, and it should still respect your visitors' consent choices.

## Common questions

### Where do I put analytics in the Next.js App Router?

In the root `app/layout.tsx`, inside `<head>`, or with `next/script` in the layout. Putting it in a page component loads it only on that page.

### Why does my analytics only count the first page in Next.js?

The tool counts full page loads and does not follow client-side navigation. Use one that listens to History API changes, or send a pageview on each route change yourself.

### Should I use next/script or a plain script tag?

Either works for a small deferred script. A plain tag in the root layout is the simplest. `next/script` adds loading strategies if you need them.

## Sources

- Next.js documentation: [Script component](https://nextjs.org/docs/app/api-reference/components/script). Checked October 2026.

## Where Datacenter fits

Datacenter's script follows Next.js client-side navigation on its own, needs no package, and its Events API takes signups and orders from your route handlers with an order id so nothing counts twice. More: https://datacenter.so/docs/events-api
