Next.js integration
Split-URL experiments (server-side 307, zero flicker), HTML experiments, and
goal tracking — one proxy file and two components. You need your project ID
from the dashboard (see Onboarding); replace
{YOUR_PROJECT_ID} below with it.
npm install @testa-soft/next1. Create the proxy
proxy.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.
// proxy.ts — Next.js 16
import { NextResponse, type NextFetchEvent, type 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 async function proxy(request: NextRequest, event: NextFetchEvent) {
try {
return await testa(request, event);
} catch (error) {
console.error("[testa] proxy failed, passing through:", error);
return NextResponse.next();
}
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|api).*)"],
};Next.js 13–15: name the file
middleware.tsand the exportmiddleware.
What's the config? Everything you set up in the Testa dashboard —
experiments, variations, their changes, goals, targeting — is compiled into a
single JSON document (the config) when you hit publish. The proxy and
<TestaProvider/> fetch it by projectId from Testa's CDN; it is the one
input that drives all of their behavior, so "publishing a change" means
"serving a new config".
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):
cache | Behavior |
|---|---|
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. |
false | No server-side caching — every request fetches. |
2. Enable HTML tests
Add two components to your root layout: <TestaGuard/> (anti-flicker, only
renders when there is something to hide) and <TestaProvider/> (applies HTML
experiments client-side and arms goal tracking).
// app/layout.tsx
import { TestaGuard, TestaProvider } from "@testa-soft/next/server";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<TestaGuard selector="body" timeoutMs={4000} />
</head>
<body>
{children}
<TestaProvider projectId="{YOUR_PROJECT_ID}" />
</body>
</html>
);
}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:
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.