Privatrak

Documentation

Set up Privatrak, name what matters to you and read every report.

Watch the video seriesEight short videos covering the whole product, on YouTube.Opens in a new tab

Install on Next.js

The package @privatrak/next adds cookieless, privacy-friendly analytics to your Next.js app. It records page views, clicks and form submissions, client-side navigation included, and it reports the AI services and crawlers that read your pages. It works with the App Router and the Pages Router.

You install the package, add your website key and render one component. Your first page views arrive in Privatrak within a few seconds.

Before you start

  • A Privatrak project for your site. Getting started shows how to create one.
  • Next.js 13.4 or newer and React 18.2 or newer.
  • Your website key, the key of the type Your website. Copy it under Settings › API Keys. It can only send data, so it is safe to put in your app.

Add analytics to your Next.js app

  1. Install the package:
    npm install @privatrak/next
  2. Add your website key to .env.local, or to your host’s environment settings:
    NEXT_PUBLIC_PRIVATRAK_API_KEY=your-website-key
  3. Render <Privatrak /> once, in the root layout. It renders nothing and follows page changes by itself:
    // app/layout.tsx
    import { Privatrak } from '@privatrak/next';
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            <Privatrak />
            {children}
          </body>
        </html>
      );
    }

With the Pages Router, render it in pages/_app.tsx instead:

// pages/_app.tsx
import type { AppProps } from 'next/app';
import { Privatrak } from '@privatrak/next';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Privatrak />
      <Component {...pageProps} />
    </>
  );
}

Next.js reads NEXT_PUBLIC_ values when it builds your app, so build again after you change the key.

From then on, Privatrak records page views, clicks and form submissions.

Change what the tracker records

<Privatrak /> takes the tracker’s options as props:

<Privatrak autocapture={false} excludedUrls={['/admin/*']} traits={['beta']} />

Options you leave out follow your project’s settings under Settings › Tracker. The props of the first render are used, so changing one later changes nothing. The Tracker reference lists every option.

Name actions in your code

Add data-track to a button or link. Privatrak then shows it as a feature, a name for something people do on your site, here signup-cta:

<button data-track="signup-cta" data-track-plan="pro">Sign up</button>

To record an event from code, import track() from @privatrak/next. setTraits() gives the visit a list of traits, such as a plan:

'use client';
import { useEffect } from 'react';
import { track, setTraits } from '@privatrak/next';

export function CheckoutButton({ plan }: { plan: string }) {
  useEffect(() => {
    setTraits([plan]);
  }, [plan]);

  return <button onClick={() => track('checkout-completed', { plan })}>Buy</button>;
}

Call both from client components and event handlers. They do nothing on the server. Name things in your code has more examples.

Report AI services and crawlers

AI services such as ChatGPT and Claude read your pages without running JavaScript, so the tracker never sees them. withPrivatrak() reports their requests from your server, and the AI services page shows how often each one read your pages. Your pages are answered exactly as before: it never changes a response or makes a visitor wait.

Create proxy.ts next to app, or in src. With Next.js 16 it reads:

import { withPrivatrak } from '@privatrak/next/server';

export const proxy = withPrivatrak();

With Next.js 13 to 15, name the file middleware.ts and write export default withPrivatrak();.

The proxy uses the same website key as the tracker.

Use it with a proxy you already have

If you already have a proxy or middleware, pass it in. Its answer is returned unchanged:

import { NextResponse, type NextRequest } from 'next/server';
import { withPrivatrak } from '@privatrak/next/server';

export const proxy = withPrivatrak((request: NextRequest) => {
  if (request.nextUrl.pathname === '/old') {
    return NextResponse.redirect(new URL('/new', request.url));
  }
  return NextResponse.next();
});

Middleware from a library wraps the same way, for example withPrivatrak(clerkMiddleware()).

Keep the pages crawlers ask for

Next.js runs the proxy for every request unless config.matcher says otherwise. This matcher skips images, scripts and other files, and keeps the pages crawlers ask for:

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:png|jpg|jpeg|gif|webp|avif|svg|ico|css|js|woff2?)$).*)',
  ],
};

Keep robots.txt, sitemaps such as sitemap.xml, llms.txt and .md files in your matcher, because crawlers request them by name. The example matcher in the Next.js documentation leaves out robots.txt and sitemap.xml, so don’t copy it. Privatrak counts exactly the requests your proxy runs for.

Set the key and options for the server

The proxy reads PRIVATRAK_API_KEY first and NEXT_PUBLIC_PRIVATRAK_API_KEY second. Use PRIVATRAK_API_KEY when only the server should have the key. It is read while the server runs, so changing it needs no rebuild. Pass options with withPrivatrak(options), or with withPrivatrak(handler, options) if you have a proxy of your own:

OptionDefaultWhat it does
apiKeyPRIVATRAK_API_KEY, then NEXT_PUBLIC_PRIVATRAK_API_KEYYour website key. Without one, the proxy reports nothing and logs one warning.
apiHostPRIVATRAK_API_HOST, then NEXT_PUBLIC_PRIVATRAK_API_HOST, then https://api.privatrak.comThe address of the Privatrak API.
flushIntervalMs2000How long a request waits for others to share its call, in milliseconds. A call holds up to 100 requests.

Host Next.js yourself

On Vercel and other serverless hosts, nothing more is needed. The report is sent before the function stops, with proxy.ts on Node and with middleware.ts on the Edge runtime.

If you host Next.js yourself behind a reverse proxy, pass the original Host header on and set X-Forwarded-Proto. In nginx, that is proxy_set_header Host $host;. Otherwise Privatrak reports your pages under your upstream server’s name.

If you already report server requests another way, for example with a Cloudflare Worker, use only one of them, or each request counts twice. Report server requests explains how.

What Privatrak records on Next.js

In the browser, Privatrak records page views, clicks, form submissions and the events you send with track(). It never reads what a visitor types into a form. It sets no cookies, so you need no cookie banner. Privacy explains how visitors are counted.

From your server, Privatrak receives only requests from known crawlers, and only for pages, robots.txt, sitemaps, llms.txt and .md files. For each request that is the crawler’s user agent, the page address and the time. Nothing about your visitors leaves your server. What leaves your server has the details.

Check that it works

Check page views

  1. Open a page of your site in your browser.
  2. In Privatrak, open Events and click Refresh events.

Your page view is the top row, with the type Pageview. If nothing arrives, open your browser’s console. The tracker says there what went wrong, and When nothing is sent tells you what to do.

Check crawler requests

  1. Request a page of your live site the way an AI service would. Replace the address with your own:
    curl -A "ChatGPT-User/1.0" https://example.com/
  2. In Privatrak, open AI services and switch to the tab All crawlers.

ChatGPT-User (OpenAI) appears under AI answers with 1 request. This test request counts like a real one. If nothing appears, check that your website key is set, that your site’s domain is in your allowed domains, that your matcher includes the page, and that no cache answered the request.

Next steps

  • Features: see how often each named action is used.
  • AI services: see which AI services read which of your pages.
  • Funnels: see where visitors drop off on the way to a goal.
  • Tracker reference: every option and function, to look up.
Next.js analytics: cookieless tracking and AI crawlers