Skip to content
pixelary Docs

Next.js

next/script

Load the script once in the root layout with next/script. Pixelary follows client-side navigation, so every route change is a page view.

1 App Router

Add both scripts to the root layout. The first one queues calls made before Pixelary has loaded.

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

const PIXELARY = "https://cdn.pixelary.app/s/px_7hq2lm4x.js";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="pixelary-queue" strategy="afterInteractive">
          {`window.pixelary = window.pixelary || function () {
            (window.pixelary.q = window.pixelary.q || []).push(arguments);
          };`}
        </Script>
        <Script src={PIXELARY} strategy="afterInteractive" />
      </body>
    </html>
  );
}

2 Pages Router

With the Pages Router, load both scripts in _app.tsx:

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

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <Script id="pixelary-queue" strategy="afterInteractive">
        {`window.pixelary = window.pixelary || function () {
          (window.pixelary.q = window.pixelary.q || []).push(arguments);
        };`}
      </Script>
      <Script src="https://cdn.pixelary.app/s/px_7hq2lm4x.js" strategy="afterInteractive" />
    </>
  );
}

3 Send conversions

Call window.pixelary from client components when something worth reporting happens. Pass your own order ID as event_id if your backend sends the same purchase to the platforms: the platform then counts it once.

TSX
"use client";

export function CheckoutButton({ order }: { order: Order }) {
  async function pay() {
    const paid = await checkout(order);
    window.pixelary("track", "Purchase",
      { value: paid.total, currency: paid.currency, order_id: paid.id },
      { event_id: paid.id, user: { email: paid.email } });
  }
  return <button onClick={pay}>Pay</button>;
}

Declare the global once for TypeScript:

TypeScript
// pixelary.d.ts
declare global {
  interface Window {
    pixelary: (command: string, ...args: unknown[]) => void;
  }
}
export {};
Don’t call window.pixelary in server components or during server rendering: it exists only in the browser.

Content Security Policy

If you set a CSP in next.config.js or middleware, allow https://cdn.pixelary.app in script-src and https://api.pixelary.app in connect-src, plus the pixels you use. The full list is in the HTML snippet guide.

Pixelary sends nothing a visitor has declined. In the EEA, the UK and Switzerland it waits for the visitor’s yes before any pixel loads or any event leaves the page.

Visitors from Europe need a consent banner

Without a banner, Pixelary never gets a yes from visitors in the EEA, the UK and Switzerland, so they are not tracked.

Pixelary reads these on its own, with no extra setup:

  • Google Consent Mode v2
  • IAB TCF 2.2
  • Global Privacy Control
  • Cookiebot
  • OneTrust
  • CookieYes
  • Usercentrics
  • Complianz
  • iubenda
  • Termly
  • Osano
  • CookieFirst
  • Didomi (via TCF)
  • Framer cookie banner

Another banner can pass its answer with one line of code. Consent & banners

Something here doesn’t match what you see? Write to hello@pixelary.app.