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.
On this page
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 Sporetag on Next.js
- In Sporetag, add your site's address and copy the tag from Set up your site.
- Open the root layout:
app/layout.tsxwith the App Router, orpages/_document.tsxwith the Pages Router. - Add the tag inside
<head>, as shown below. - Deploy the change.
- Click Check my site in Sporetag, then open the deployed site.
With the App Router:
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<script defer data-site="yoursite.com" src="https://sporetag.com/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. Sporetag'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:
script-src 'self' https://sporetag.com;
connect-src 'self' https://sporetag.com;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 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
_st_idcookie 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 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. Checked October 2026.
Spotted something wrong or out of date? Email hello@sporetag.com and we will fix it.