> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pivolio.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pivolio.com/_mcp/server.

# Next.js SDK

`@pivolio/next` is the only Pivolio package a Next.js app installs. It
re-exports the entire [`@pivolio/react`](/documentation/sd-ks/react) surface — `PivolioProvider`,
`usePivolio`, `useTrack`, `usePageView`, the types, the enums and the `Tracker`
class — and adds one component of its own: `<PivolioPageView />`.

## Install

```bash
npm install @pivolio/next
```

Import everything from `@pivolio/next`. Adding `@pivolio/react` or
`@pivolio/browser` as direct dependencies buys you nothing and risks importing
the singleton API by accident, which would give the page
[two independent trackers](/documentation/sd-ks/react#why-the-singleton-api-is-not-re-exported).

## App Router setup

The provider builds its tracker in an effect, so it must live in a client
component. Wrap it once and mount it in the root layout.

```tsx
'use client';

import { PivolioProvider, PivolioPageView } from '@pivolio/next';
import type { ReactNode } from 'react';

export function Analytics({ children }: { children: ReactNode }) {
  return (
    <PivolioProvider
      config={{
        writeKey: process.env.NEXT_PUBLIC_PIVOLIO_WRITE_KEY as string,
        apiUrl: 'https://api.pivolio.com',
      }}
    >
      <PivolioPageView />
      {children}
    </PivolioProvider>
  );
}
```

```tsx
import { Analytics } from './analytics';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Analytics>{children}</Analytics>
      </body>
    </html>
  );
}
```

Because the layout itself stays a server component, only the small `Analytics`
wrapper crosses into the client bundle. Your write key is public by design —
write keys can only ingest events, never read data or change settings — so
`NEXT_PUBLIC_` is the right prefix.

## PivolioPageView

`<PivolioPageView />` fires a page view on App Router navigation. Place it
**inside** the provider; it renders nothing.

By default it watches `usePathname()` only. It also fires the initial view: on
mount the tracker is still `null` and the call no-ops, but the callback changes
identity exactly once — when the tracker mounts — which fires that first view.

> **Note**
>
> Don't set `autoPageView` or `trackSPANavigation` in the config alongside this
> component. `<PivolioPageView />` is the App-Router-aware version of the same job,
> and combining them double-counts every navigation.

### countSearchParams

```tsx
<PivolioPageView countSearchParams />
```

Set this on routes where the query string is genuinely a new page — a search
results page, say — and you want `/search?q=shoes` and `/search?q=boots` counted
separately. It defaults to `false` because on most routes the query string
carries filter toggles and tracking params, and counting them would report every
checkbox click as a page view.

> **Warning**
>
> `countSearchParams` reads `useSearchParams()`, which **opts the surrounding
> route out of static rendering** up to the nearest `<Suspense>` boundary. Wrap the
> component when you enable it:
>
> ```tsx
> import { Suspense } from 'react';
>
> <Suspense fallback={null}>
>   <PivolioPageView countSearchParams />
> </Suspense>
> ```

The default path never calls `useSearchParams()` at all — that's why the two
modes are separate components internally — so `<PivolioPageView />` on its own
leaves your static routes static.

## Tracking from a component

Everything from the React bindings works unchanged:

```tsx
'use client';

import { useTrack } from '@pivolio/next';

export function AddToCart({ sku, price }: { sku: string; price: number }) {
  const { track } = useTrack();

  return (
    <button
      onClick={() =>
        track('add_to_cart', {
          properties: { value: price, currency: 'USD', sku },
        })
      }
    >
      Add to cart
    </button>
  );
}
```

> **Note**
>
> `useTrack()` exposes four callbacks — `track`, `identify`, `trackPurchase` and
> `page`. The other commerce helpers aren't on it; reach for
> [`usePivolio()`](/documentation/sd-ks/react) to get the tracker instance and call
> `trackAddToCart` or `trackBeginCheckout` directly.

## Server-side events

The SDK is browser-only. To send events from a route handler, a server action, or
a webhook, `POST` to the collector directly with your write key — see the
[HTTP API guide](/documentation/guides/http-api). That's also the more reliable path for
purchases: a server-side confirmation is immune to ad-blockers and to the user
closing the tab before the beacon fires.

## Next steps

* [React SDK](/documentation/sd-ks/react) — the provider and hooks in detail.
* [Configuration](/documentation/sd-ks/configuration) — every `SDKConfig` field.
* [Identity and consent](/documentation/sd-ks/identity-consent) — cookies, linker, opt-out.