Documentation menu: Next.js
Next.js integration
Add theBizness.ai analytics to your Next.js application with one script tag in your root layout.
Setup time
2 minutes
Difficulty
Easy
Loading
After the page is interactive
Choose your Next.js version
App Router installation
Step 1: Add to the root layout
Add the tracking script to your app/layout.tsx file:
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<Script
strategy="afterInteractive"
data-website-id="your-website-id"
data-domain="yourdomain.com"
src="https://thebizness.ai/js/script.js"
/>
</head>
<body>{children}</body>
</html>
)
}strategy="afterInteractive" loads the script after the page becomes interactive, so it does not delay your first render.
Pages Router installation
Step 1: Add to _app.tsx
Add the tracking script to your pages/_app.tsx file (_app.js works the same way):
// pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Script
strategy="afterInteractive"
data-website-id="your-website-id"
data-domain="yourdomain.com"
src="https://thebizness.ai/js/script.js"
/>
<Component {...pageProps} />
</>
)
}Do not put this tag in pages/_document. That file is rendered on the server only and is never hydrated, so a next/script tag with strategy="afterInteractive" there does not load in the browser.
Environment variables (recommended)
Step 1: Add to .env.local
# .env.local
NEXT_PUBLIC_THEBIZNESS_WEBSITE_ID=your-website-id
NEXT_PUBLIC_THEBIZNESS_DOMAIN=yourdomain.comStep 2: Use them in the layout
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<Script
strategy="afterInteractive"
data-website-id={process.env.NEXT_PUBLIC_THEBIZNESS_WEBSITE_ID}
data-domain={process.env.NEXT_PUBLIC_THEBIZNESS_DOMAIN}
src="https://thebizness.ai/js/script.js"
/>
</head>
<body>{children}</body>
</html>
)
}- • Use a different website ID for staging and production
- • Change the ID without editing code
- • Keep your configuration in one place
The website ID and domain are public: they appear in your page HTML, and NEXT_PUBLIC_ variables are included in the browser bundle. They are configuration, not secrets.
Advanced: proxy setup
- • Served from your own domain, so ad blockers are less likely to block it. Some blockers still will.
- • Keeps the script and its requests on your first-party domain
- • The proxy removes the Cookie and Authorization headers before forwarding, and the Referer on the script download, so your visitors' cookies for your own domain and the address of the page that loads the script never reach us
- • It keeps the Origin (or Referer) header on every request under
/api/events/. The origin check needs one of them: a browser request that carries neither, or one from another domain, is rejected and nothing is recorded - • Every visitor then reaches us from your server's address, so the per-address rate limits (120 pageviews a minute, for example) apply to your site as a whole
- • Conversions work through the same proxy: the script sends them to
/api/events/conversionsand the recipes pass them on to/api/conversions. The/checkpath only serves copies of the script cached before October 2026 - • Server-side reporting with your ingest secret goes straight to https://thebizness.ai, not through this proxy, which removes the Authorization header. Keep the secret out of browser code
Step 1: Add the proxy
Forward the script and the tracking requests to thebizness.ai. Pick the one that matches how your site is served. If you already have a middleware.ts, add this logic to it and add the two paths to its matcher.
// middleware.ts (project root, or src/ if you use a src directory)
import { NextResponse, type NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname, search } = request.nextUrl;
// The script posts conversions to /api/events/conversions. Our API serves them at /api/conversions.
const target = pathname.startsWith('/api/events/conversions')
? pathname.replace('/api/events/conversions', '/api/conversions')
: pathname;
// Do not pass your visitors' cookies or any HTTP authentication on to us
const headers = new Headers(request.headers);
headers.delete('cookie');
headers.delete('authorization');
// The script file does not need the address of the page that loads it
if (pathname === '/js/script.js') headers.delete('referer');
// Origin (and the origin-only Referer of the script's requests) stay intact: the origin check reads them
return NextResponse.rewrite(new URL(target + search, 'https://thebizness.ai'), {
request: { headers },
});
}
export const config = {
matcher: ['/js/script.js', '/api/events/:path*'],
};Next.js 16 and later name this file convention proxy (proxy.ts, with an exported function called proxy). The code inside is the same.
# /etc/nginx/snippets/thebizness-proxy.conf
proxy_ssl_server_name on;
proxy_set_header Host thebizness.ai;
# An empty value means the header is not passed on
proxy_set_header Cookie "";
proxy_set_header Authorization "";
# Inside the server { } block of your site
location = /js/script.js {
include snippets/thebizness-proxy.conf;
proxy_set_header Referer "";
proxy_pass https://thebizness.ai/js/script.js;
}
location = /api/events {
include snippets/thebizness-proxy.conf;
proxy_pass https://thebizness.ai/api/events;
}
location = /api/events/event {
include snippets/thebizness-proxy.conf;
proxy_pass https://thebizness.ai/api/events/event;
}
location = /api/events/conversions {
include snippets/thebizness-proxy.conf;
proxy_pass https://thebizness.ai/api/conversions;
}
# Optional: only copies of the script cached before October 2026 still call this
location = /api/events/conversions/check {
include snippets/thebizness-proxy.conf;
proxy_pass https://thebizness.ai/api/conversions/check;
}Reload nginx after adding the snippet and the locations.
// Cloudflare Worker. Add routes for:
// yourdomain.com/js/script.js
// yourdomain.com/api/events*
const UPSTREAM = 'https://thebizness.ai';
export default {
async fetch(request) {
const url = new URL(request.url);
// The script posts conversions to /api/events/conversions. Our API serves them at /api/conversions.
const path = url.pathname.startsWith('/api/events/conversions')
? url.pathname.replace('/api/events/conversions', '/api/conversions')
: url.pathname;
// Do not pass your visitors' cookies or any HTTP authentication on to us
const headers = new Headers(request.headers);
headers.delete('cookie');
headers.delete('authorization');
// The script file does not need the address of the page that loads it
if (url.pathname === '/js/script.js') headers.delete('referer');
return fetch(new Request(UPSTREAM + path + url.search, {
method: request.method,
headers,
body: request.body,
redirect: 'manual',
}));
},
};Attach the Worker only to the two routes in the comment, and make sure your site has no routes of its own under /api/events.
Step 2: Update the script source
Change the script src to use your local domain:
// With proxy configuration, use local paths
<Script
strategy="afterInteractive"
data-website-id="your-website-id"
data-domain="yourdomain.com"
src="/js/script.js"
/>Next.js rewrites() in next.config.js work, but they forward everything the browser sends: your visitors' cookies for your domain, any HTTP Basic-auth header, and the address of the page, all of which would reach thebizness.ai. Use the middleware above instead. If you keep rewrites anyway, this is the configuration:
// next.config.js (not recommended: this forwards everything the browser sends)
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/js/script.js',
destination: 'https://thebizness.ai/js/script.js',
},
{
source: '/api/events',
destination: 'https://thebizness.ai/api/events',
},
{
source: '/api/events/event',
destination: 'https://thebizness.ai/api/events/event',
},
{
source: '/api/events/conversions',
destination: 'https://thebizness.ai/api/conversions',
},
// Optional: only copies of the script cached before October 2026 still call this
{
source: '/api/events/conversions/check',
destination: 'https://thebizness.ai/api/conversions/check',
},
];
},
};
module.exports = nextConfig;Verify installation
Quick check
- 1. Deploy your changes
- 2. Visit your website
- 3. Check browser Network tab for script load
- 4. Navigate between pages
Dashboard check
- 1. Sign in to your theBizness.ai dashboard
- 2. Open the Overview and check that pageviews appear after a reload
- 3. Navigate between pages and confirm each visit is counted
- 4. Test UTM parameter tracking
- • The tracker does nothing on localhost, *.local, *.test, private IP addresses or file:// pages. It logs a "Tracking disabled on localhost" warning in the console. Verify on a publicly reachable domain.
- • Make sure you're using your actual website ID, not the placeholder
- • Check that your domain matches exactly (no www mismatch)
- • Verify the script loads in browser DevTools Network tab
- • If your site sends a Content-Security-Policy header, allow https://thebizness.ai in script-src and connect-src. Blocked requests show only as CSP errors in the browser console
Installation complete
Your Next.js application is now tracking visitor analytics with theBizness.ai. Here's what to do next:
Revenue attribution
Connect Stripe to track which campaigns generate paying customers
Conversion tracking
Set up custom events to track signups, purchases, and more
UTM campaigns
Start using UTM parameters to track marketing campaigns