pieowl

How to add analytics to Next.js

Add cookie-free analytics to your Next.js app in a few minutes with Pieowl. Track page views, custom events and users.

Pieowl TeamBeginner · 8 min · 8 Oct 2026
Guide · Next.js

This guide shows you how to add Pieowl to a Next.js app. When you finish, you will have automatic page views, custom event tracking and user identification.

Pieowl works with both the App Router and the Pages Router. The tracker never sets a cookie, so in many cases you don't need a consent banner for your analytics. See when you still do.

Prerequisites

Get your snippet

There is no package to install for the browser. Pieowl is one script tag, loaded from pieowl.com. In Next.js you add it with the built-in next/script component.

The snippet has two parts. The first line creates a small queue, so calls made before pieowl.js loads are sent once it's ready. The second loads the script with your Client ID.

Add Pieowl to your layout

Add the script to the root of your app, so it loads on every page.

For the App Router, put it inside the body tag of your root layout. Pieowl records a page view each time the user moves to a new page, including client-side navigation.

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="pieowl-queue">{`window.pieowl=window.pieowl||function(){(window.pieowl.q=window.pieowl.q||[]).push(arguments)};`}</Script>
        <Script src="https://pieowl.com/pieowl.js" data-client-id="your-client-id" strategy="afterInteractive" />
      </body>
    </html>
  );
}

For the Pages Router, add the same two scripts to _app.tsx.

// pages/_app.tsx
import type { AppProps } from 'next/app';
import Script from 'next/script';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script id="pieowl-queue">{`window.pieowl=window.pieowl||function(){(window.pieowl.q=window.pieowl.q||[]).push(arguments)};`}</Script>
      <Script src="https://pieowl.com/pieowl.js" data-client-id="your-client-id" strategy="afterInteractive" />
      <Component {...pageProps} />
    </>
  );
}

By default Pieowl also tracks clicks on links to other sites and on elements with a data-track attribute. You can turn each of these on or off in Settings → Site → What Pieowl counts, with no code to edit.

Track AI crawlers (optional)

AI crawlers and search bots don't run JavaScript, so the script never sees them. To count them, add @pieowl/bots on the server. It needs your client secret, so keep both keys in environment variables.

npm install @pieowl/bots
// proxy.ts
import { trackBots } from '@pieowl/bots/next';

export const proxy = trackBots({
  clientId: process.env.PIEOWL_CLIENT_ID!,
  clientSecret: process.env.PIEOWL_CLIENT_SECRET!,
});

On Next.js 15 or older, name the file middleware.ts and export it as middleware.

Track custom events

Page views are only part of the story. To see how people use your product, track custom events such as button clicks, form submits or feature use.

In client components, call window.pieowl('track'). It works anywhere in your component tree.

'use client';

export function SignupButton() {
  return (
    <button
      type="button"
      onClick={() => window.pieowl('track', 'signup_clicked', { location: 'header' })}
    >
      Sign up
    </button>
  );
}

With TypeScript, declare window.pieowl once, in any .d.ts file.

// pieowl.d.ts
declare global {
  interface Window {
    pieowl: (method: string, ...args: any[]) => void;
  }
}

export {};

To track on the server, install @pieowl/sdk and create a Pieowl instance with your client secret. Use it in route handlers, webhooks or server actions.

npm install @pieowl/sdk
// lib/pieowl.ts
import { Pieowl } from '@pieowl/sdk';

export const pieowl = new Pieowl({
  clientId: process.env.PIEOWL_CLIENT_ID!,
  clientSecret: process.env.PIEOWL_CLIENT_SECRET!,
});

Never expose your client secret in the browser. Keep it in server-only code.

Now you can track events from anywhere on the server.

// app/api/webhook/route.ts
import { pieowl } from '@/lib/pieowl';

export async function POST(request: Request) {
  const data = await request.json();

  await pieowl.track('webhook_received', {
    source: data.source,
    event_type: data.type,
  });

  return Response.json({ success: true });
}

On Vercel or another serverless platform, await the call or wrap it in waitUntil. That way the event is sent before the function stops.

Identify users

Anonymous data is useful. Knowing who did what is more useful. When you identify users, you can follow them across sessions, filter by their properties and build cohorts.

In client components, call identify after a user logs in.

'use client';

import { useEffect } from 'react';

export function UserProfile({ user }) {
  useEffect(() => {
    window.pieowl('identify', {
      profileId: user.id,
      firstName: user.firstName,
      lastName: user.lastName,
      email: user.email,
      properties: {
        plan: user.plan,
      },
    });
  }, [user]);

  return <div>Welcome, {user.firstName}!</div>;
}

If you load the user on the server, pass it as a prop to a small client component like this one, for example in app/dashboard/layout.tsx.

When the user logs out, call window.pieowl('clear') so new events are not linked to them.

Verify your setup

Open your app in the browser and visit a few pages. Then open the Pieowl dashboard and check the realtime view. Page views should show up within seconds.

No events? Check the browser console for errors. The most common causes are a wrong Client ID or an ad blocker. You can get around ad blockers by sending events through your own domain.

To do that, install @pieowl/bots and add a catch-all route that forwards requests to Pieowl.

// app/api/pieowl/[...pieowl]/route.ts
import { createRouteHandler } from '@pieowl/bots/route-handler';

export const { GET, POST } = createRouteHandler();

Then point the script at that route with the init form: set apiUrl in init, and load /api/pieowl/pieowl.js without data-client-id.

<Script id="pieowl-init">{`window.pieowl=window.pieowl||function(){(window.pieowl.q=window.pieowl.q||[]).push(arguments)};
window.pieowl('init', { clientId: 'your-client-id', apiUrl: '/api/pieowl' });`}</Script>
<Script src="/api/pieowl/pieowl.js" strategy="afterInteractive" />

Now the script and the events load from your own domain, so browser extensions that block third-party analytics don't see them.

Next steps

The script tag reference covers more options, such as global properties, event filters and profile counters. The Next.js docs cover bot tracking and the proxy. To learn how Pieowl measures visits without cookies, read cookieless analytics.

Does Pieowl work with both App Router and Pages Router?

Yes. The setup is almost the same. Add the script to your root layout for the App Router, or to _app.tsx for the Pages Router. Every tracking feature works the same way in both.

Does Pieowl use cookies?

No. The tracker never sets a cookie and stores nothing on your visitors' devices. In many cases that means no consent banner for your analytics. See Do you need a cookie banner?. Visitors are counted with a hash that changes every day.

Does Pieowl help with GDPR?

Pieowl is built for GDPR: no cookies, no stored IPs. The IP address is used for the country lookup and then dropped. Your data is stored in the EU. Ask your legal advisor about your own setup.

How do I avoid ad blockers?

Add the proxy route handler shown in the verify step. Tracking requests then go through your own domain, which ad blockers usually leave alone.

Your first dashboard in five minutes.

14-day free trial, no card needed. Cloud in the EU. No cookies.