Next.js SDK

The React bindings plus App Router page views.

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

Install

$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.

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.

1'use client';
2
3import { PivolioProvider, PivolioPageView } from '@pivolio/next';
4import type { ReactNode } from 'react';
5
6export function Analytics({ children }: { children: ReactNode }) {
7 return (
8 <PivolioProvider
9 config={{
10 writeKey: process.env.NEXT_PUBLIC_PIVOLIO_WRITE_KEY as string,
11 apiUrl: 'https://api.pivolio.com',
12 }}
13 >
14 <PivolioPageView />
15 {children}
16 </PivolioProvider>
17 );
18}
1import { Analytics } from './analytics';
2
3export default function RootLayout({ children }: { children: React.ReactNode }) {
4 return (
5 <html lang="en">
6 <body>
7 <Analytics>{children}</Analytics>
8 </body>
9 </html>
10 );
11}

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.

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

1<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.

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:

1import { Suspense } from 'react';
2
3<Suspense fallback={null}>
4 <PivolioPageView countSearchParams />
5</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:

1'use client';
2
3import { useTrack } from '@pivolio/next';
4
5export function AddToCart({ sku, price }: { sku: string; price: number }) {
6 const { track } = useTrack();
7
8 return (
9 <button
10 onClick={() =>
11 track('add_to_cart', {
12 properties: { value: price, currency: 'USD', sku },
13 })
14 }
15 >
16 Add to cart
17 </button>
18 );
19}

useTrack() exposes four callbacks — track, identify, trackPurchase and page. The other commerce helpers aren’t on it; reach for usePivolio() 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. 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