TestaDocs
Docs/Integration/Next.js (Pages Router)

Next.js integration (Pages Router)

The same server-side split-URL redirects, HTML experiments, and goal tracking as the App Router integration — one middleware file and one component in _app. You need your project ID from the dashboard (see Get your script); replace {YOUR_PROJECT_ID} below with it.

bash
npm install @testa-soft/next

Requires v1.3.0 or later

The @testa-soft/next/pages entry point was added in v1.3.0. On the App Router, use the App Router guide instead — a given app uses one or the other, never both.

1. Create the middleware

middleware.ts at the project root (or under src/). It buckets each visitor server-side, writes the sticky cookie, and issues split-URL redirects before any HTML is sent — identical to the App Router; the proxy is router-agnostic.

ts
// middleware.ts
import type { NextFetchEvent, NextRequest } from "next/server";
import { createTestaProxy } from "@testa-soft/next";

const testa = createTestaProxy({
  projectId: "{YOUR_PROJECT_ID}",
  secureCookies: process.env.NODE_ENV === "production",
  // cache: "per-pageload", // config caching — see the table below
});

export function middleware(request: NextRequest, event: NextFetchEvent) {
  return testa(request, event);
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|api).*)"],
};

Caching behavior determines how long it may take for Admin panel changes to propagate to your site. It's controlled by the cache option of createTestaProxy (shown commented out in the snippet above):

cacheBehavior
true (default)Cached 60s, then served stale while revalidating in the background — zero request latency, publishes live within ~1 min.
'per-pageload'Every hard page load fetches a fresh config (publishes live on the next pageview); soft navigations keep the config pinned for the session.

2. Add the provider

Add <TestaProvider/> once in pages/_app.tsx. It applies HTML experiments, arms goal tracking, manages the anti-flicker shield, and re-points next/link navigations that the middleware cannot see.

tsx
// pages/_app.tsx
import type { AppProps } from "next/app";
import { TestaProvider } from "@testa-soft/next/pages";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <TestaProvider projectId="{YOUR_PROJECT_ID}">
      <Component {...pageProps} />
    </TestaProvider>
  );
}

That is the whole integration. The config is fetched once per page load and shared by everything the provider wires up — soft navigations never refetch it.

3. Goals

Create goals in the dashboard (Goals tracking). Page-view and click goals need no code. Custom-event goals fire when your code sends a matching event:

ts
import { pushEvent } from "@testa-soft/next";

pushEvent("signup_completed", { plan: "pro" });

Also available as window.testa.pushEvent(...) and — legacy-compatible — window.Analytica.pushEvent(...). Each goal counts once per visitor.