Skip to main content

Statsig in Next.js

Installation

To use Statsig with Next.js, install the @statsig/react-bindings and @statsig/web-analytics packages:

npm install @statsig/react-bindings @statsig/web-analytics

Add your Statsig client key to your .env.local file. You can find your key at which you can find at console.statsig.com/api_keys.

Note: The NEXT_PUBLIC_ prefix is required for this to be available on the client side.

# .env.local
NEXT_PUBLIC_STATSIG_CLIENT_KEY=client-<REPLACE_WITH_YOUR_CLIENT_KEY>

Basic Usage with Next.js

Statsig supports both the Page Router and App Router in Next.js. There are some differences in how you integrate Statsig into each.

To integrate Statsig into your App Router app you need to use the use client directive to render the StatsigProvider on the client side.

// app/my-statsig.tsx

"use client";

import React, { useEffect } from "react";
import {
LogLevel,
StatsigProvider,
} from "@statsig/react-bindings";
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';

export default function MyStatsig({ children }: { children: React.ReactNode }) {
return (
<StatsigProvider
sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
user={{ userID: "a-user" }}
options={{
logLevel: LogLevel.Debug,
plugins: [ new StatsigAutoCapturePlugin() ],
}}>
{children}
</StatsigProvider>
);
}

Then, use this component in your Root layout.tsx file.

// app/layout.tsx

import MyStatsig from "./my-statsig";

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

Now if you load your app, you should see events being sent to Statsig and if you have LogLevel set to Debug, you should see debug logs in the browsers console.

Note: You should remove the logLevel option before deploying your app to production.

Checking a Gate

'use client';

import { useGateValue } from "@statsig/react-bindings";

export default function Home() {
const gate = useGateValue("my_gate");

return (
<div>
Gate Value: {gate ? 'PASSED' : 'FAILED'}
</div>
);
}

Note: In an App Router app, you need to use the use client directive to ensure your logic runs on the frontend.

Getting a Dynamic Config

'use client';

import { useDynamicConfig } from "@statsig/react-bindings";

export default function Home() {
const config = useDynamicConfig("my_dynamic_config");

return (
<div>
Title: {config.get('title', 'Fallback Title')}
</div>
);
}

Note: In an App Router app, you need to use the use client directive to ensure your logic runs on the frontend.

Getting an Experiment

'use client';

import { useExperiment, useLayer} from "@statsig/react-bindings";

export default function Home() {
const layer = useLayer("my_experiment_layer");
// or
const experiment = useExperiment("my_experiment");

return (
<div>
Title: {layer.get('title', 'Fallback Title')}
{/* or */}
Title: {experiment.get('title', 'Fallback Title')}
</div>
);
}

Note: In an App Router app, you need to use the use client directive to ensure your logic runs on the frontend.

Getting a Parameter Store

'use client';

import { useParameterStore} from "@statsig/react-bindings";

export default function Home() {
const store = useParameterStore("my_param_store");

return (
<div>
Title: {store.get('title', 'Fallback Title')}
</div>
);
}

Note: In an App Router app, you need to use the use client directive to ensure your logic runs on the frontend.

Logging an Event

'use client';

import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
const { client } = useStatsigClient();

return (
<div>
<button onClick={() => client.logEvent("my_custom_event")}>
Click Me
</button>
</div>
);
}

Note: In an App Router app, you need to use the use client directive to ensure your logic runs on the frontend.

Session Replay

By including the @statsig/session-replay package in your project, you can automatically capture and log user sessions as videos. This feature is useful for debugging and understanding user behavior. Read more about Session Replay.

...

Web Analytics / Auto Capture

By including the @statsig/web-analytics package in your project, you can automatically capture common web events like clicks and page views. Read more about Web Analytics.

...

Advanced Setup

We offer deeper integrations with Next.js that improve the performance and stability of your Statsig integration. If you were just trying to log an event or check your first gate, your project is configured and ready to go with the above basic setup instructions.

Bootstrapping is a method of keeping updated values on your server (in the case of Next, your node webserver), and sending them down with the frontend code when a request is made. This has the advantage of preventing an additional network request before your content is displayed, improving your site's responsiveness. This also enables Statsig usage on server-rendered components. While the performance gains are appealing, bootstrapping requires some additional setup effort, and you must be mindful of which code you are running server side and client side.

Install statsig-node

To generate the required values, we can use the Statsig server SDK (statsig-node) on our backend.

npm install statsig-node

next, add a Server key to your .env file. Note that this one is private and does not start with NEXT_PUBLIC_. You can find your key at which you can find at console.statsig.com/api_keys.

# .env.local
NEXT_PUBLIC_STATSIG_CLIENT_KEY= # No Change
STATSIG_SERVER_KEY=secret-<REPLACE_WITH_YOUR_SERVER_KEY> # <- Added this line

Integrate the Backend Logic

In our App Router example, we should add a new file statsig-backend.ts to our app folder. This file will contain the logic to initialize our Statsig server SDK and generate the bootstrap values.

// app/statsig-backend.ts

import Statsig, { StatsigUser } from "statsig-node";

const isStatsigReady = Statsig.initialize(process.env.STATSIG_SERVER_KEY!, {
environment: { tier: "development" },
});

export async function generateBootstrapValues(): Promise<{
data: string;
user: StatsigUser;
key: string;
}> {
await isStatsigReady;

const user = { userID: "a-user" };
const key = process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!;

const values = Statsig.getClientInitializeResponse(user, key, {
hash: "djb2", //🔥 'djb2' is required. By default this would be 'sha256'.
});

return {
data: JSON.stringify(values),
user,
key,
};
}

Apply the Bootstrap Values

We now need to refactor our Root layout.tsx to call the generateBootstrapValues function and pass it to the client side.

// app/layout.tsx

// ...
import { generateBootstrapValues } from "./statsig-backend";
// ...

export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const bootstrapValues = await generateBootstrapValues();

return (
<html lang="en">
<MyStatsig bootstrapValues={bootstrapValues}>
<body>
{children}
</body>
</MyStatsig>
</html>
);
}

Finally, update the MyStatsig component to accept the bootstrapValues prop, swapping out useClientAsyncInit for useClientBootstrapInit.

// app/my-statsig.tsx

"use client";

import {
LogLevel,
StatsigProvider,
StatsigUser,
useClientBootstrapInit, // <- Add this
} from "@statsig/react-bindings";
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
import React, { useEffect } from "react";

export default function MyStatsig({
children,
bootstrapValues,
}: {
bootstrapValues: { data: string; user: StatsigUser; key: string };
children: React.ReactNode;
}) {
// Update to using useClientBootstrapInit instead of auto initializing in the provider
const client = useClientBootstrapInit(
bootstrapValues.key,
bootstrapValues.user,
bootstrapValues.data,
{
logLevel: LogLevel.debug,
plugins: [ new StatsigAutoCapturePlugin() ]
}
);

return <StatsigProvider client={client}>{children}</StatsigProvider>;
}

If you load the app now, you should see the same values as your previous implementation, this time without any additional network requests.

Managing StableIDs

Statsig generates StableIDs as a pseudo-ID for logged-out experiments and user management. StableIDs are generated client-side, but when boostrapping, values are generated on the server, creating undesirable side-effects like stableIDs regenerating more than logical for any one device/user. A simple cookie can solve this problem, with an implementation pattern suggested here.

Proxying Network Traffic (Optional)

If you want to harden your integration and protect your feature gating/experimentation values/event logging from being blocked by ad blockers, you can set up a network proxy.

It is possible to route all Statsig network traffic through your Next.js server. There are a few reasons why you might want to set this up.

  • Avoid ad blockers
  • Keep network traffic within your own cluster
  • Maintain your own event filtering/de-duplication logic

The Statsig client uses two main endpoints. /initialize and /log_event. We will need to setup Next.js Route Handlers for these. For the sake of this demo, we will house them under app/statsig-proxy.

Add Route /initialize

// app/statsig-proxy/initialize/route.ts

import { StatsigUser } from "statsig-node";

import { generateBootstrapValues } from "@/app/statsig-backend";

export async function POST(request: Request): Promise<Response> {
const json = await request.json();

if (!json || typeof json !== "object") {
return new Response(null, { status: 400 });
}

const body = json as { user: StatsigUser };

const { data } = await generateBootstrapValues(body.user);
return new Response(data);
}

Add Route /log_event

// app/statsig-proxy/log_event/route.ts

import { LogEventObject } from "statsig-node";

import { logEvents } from "@/app/statsig-backend";

type LogEventBody = {
events: LogEventObject[];
};

export async function POST(request: Request): Promise<Response> {
const json = await request.json();

if (!json || typeof json !== "object" || !Array.isArray(json.events)) {
return new Response(null, { status: 400 });
}

const body = json as LogEventBody;

await logEvents(body.events);

return new Response(JSON.stringify({ success: true }));
}

Assign Urls

Now that we have endpoints for handling /initialize and /log_event, we need to configure that Statsig client to use them.

In our app/my-statsig.tsx file, where we created our StatsigClient, we can pass in some extra StatsigOptions.

const inst = new StatsigClient(
clientSdkKey,
user,
{
networkConfig: {
logEventUrl: "/statsig-proxy/log_event",
initializeUrl: "/statsig-proxy/initialize",
},
disableCompression: true,
disableStatsigEncoding: true,

// Optional - Prints debug logs to the console
logLevel: LogLevel.Debug
}
);