Skip to main content

How to set up Feature Flags with Next.js (App Router)

Next.js, a leading React framework, provides a robust foundation for developing fast and user-friendly web applications.

This guide dives into the integration of feature flags with Next.js, leveraging Statsig's React Client SDK and NodeJS Server SDK. By incorporating feature flags into your Next.js app, you can control feature access and dynamically tailor user experiences. Follow along to learn how to set up feature flags in your Next.js project and enhance your application's flexibility and user engagement.

In this tutorial, we'll integrate our React Client SDK and NodeJS Server SDK into a simple Next.js app with App Router and check a feature gate to determine if the end-user should see a new feature.

Create a Feature Gate in Statsig for Your Next.js Project

Create a Statsig account and obtain an SDK key: Signup for free!

The below command can be run to automatically create the simple nextjs_app_bg gate in Statsig that gets used throughout this integration guide and demo app. The gate randomizes based on userID and has a 50/50 allocation. Make sure to replace the authorization key in the header with your key generated in Statsig project settings as documented here.

curl --request POST 'https://statsigapi.net/console/v1/gates' \
--header 'STATSIG-API-KEY: console-REPLACE_WITH_YOURS' \
--header 'Content-Type: application/json' \
--data-raw '{"name":"nextjs_app_bg","idType":"userID","isEnabled":true,"rules":[{"name":"Everyone","passPercentage":50,"conditions":[{"type":"public"}]}]}'

image image

Setup a Next.js app

Create a Next.js app by running the following command, and using the default options:

npx create-next-app@latest

This will create a Next.js app in a folder with the name of your app (default my-app). Run the following commands to run the app:

cd my-app
npm run dev

You should see the following app in your localhost:

image

Setup the Statsig React SDK on the client side

First install the Statsig React SDK

npm install statsig-react

And add your Statsig Client API key (available in your Project Settings) to an env variable in .env.local and to your hosting provider (e.g. Vercel, Netlify). Prefix the env variable with NEXT_PUBLIC_ so that the client SDK will be bundled to the browser.

NEXT_PUBLIC_STATSIG_CLIENT_KEY=your_client_key
// app/client-app.tsx

"use client"; // important! the distinction between what is happening on the client and server will matter for ssr

import {
StatsigProvider,
StatsigUser,
useGate,
} from "statsig-react";


function Content() {
const { value } = useGate("nextjs_app_bg");
return (
<div style={value ? {background: "#194B7D"} : {}}>
<div style={{ padding: 16 }}>nextjs_app_bg: {value ? "Passing" : "Failing"}</div>
</div>
);
}

export default function ClientApp({
user,
}: {
user: StatsigUser;
}): JSX.Element {

return (
<StatsigProvider
sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
waitForInitialization={true}
user={user}
>
<Content />
</StatsigProvider>
);
}

Now, render that client app:

// app/page.tsx

import ClientApp from "./client-app";

export default async function Index(): Promise<JSX.Element> {
const user = { userID: "123" };
return <ClientApp user={user} />;
}

You'll now be bucketed either into the gate or not and get a corresponding experience. It should look like this, depending on if the userID passes or fais the gate. Change the ID in the page.tsx to try out both experiences:

Failing experience Passing experience

Using Server Side Rendering to Improve Performance

To improve performance for the client side feature gates check pattern, we can evaluate feature flag values on the server and pass results to the client.

First install the Statsig Node SDK

npm install statsig-node

And add your Statsig Server Secret Key to your .env.local file

STATSIG_SERVER_SECRET=your_server_key

Add a utility function that can execute on the server, and generate the statsig payload for the react SDK to consume.

// utils/get-statsig-values.ts

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

const isStatsigReady = Statsig.initialize(
process.env.STATSIG_SERVER_SECRET!,
);

export async function getStatsigValues(user: StatsigUser): Promise<Record<string, unknown>> {
await isStatsigReady;

const values = Statsig.getClientInitializeResponse(user);

return values ?? {};
}

Now, update the client app component to take in the values generated by this helper. Import the StatsigSynchronousProvider, add a parameter for the values, and render that instead

// app/client-app.tsx

"use client"; // important! the distinction between what is happening on the client and server is what matters

import {
StatsigSynchronousProvider, // update the import
StatsigUser,
useGate,
} from "statsig-react";


function Content() {
const { value } = useGate("nextjs_app_bg");
return (
<div style={value ? {background: "#194B7D"} : {}}>
<div style={{ padding: 16 }}>nextjs_app_bg: {value ? "Passing" : "Failing"}</div>
</div>
);
}

export default function ClientApp({
user,
values,
}: {
user: StatsigUser;
values: Record<string, unknown>;
}): JSX.Element {

return (
<StatsigSynchronousProvider
sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
initializeValues={values}
user={user}
>
<Content />
</StatsigSynchronousProvider>
);
}

Now in our page.tsx, we can evaluate Statsig configs for a user on the server side, and pass the results to the StatsigSynchronousProvider. This will avoid a round-trip to Statsig for gate evaluation results, and avoiding flicker when loading webpages.

// app/page.tsx

import { getStatsigValues } from "../utils/get-statsig-values";
import ClientApp from "./client-app";

export default async function Index(): Promise<JSX.Element> {
const user = { userID: "123" };
const values = await getStatsigValues(user);
return <ClientApp user={user} values={values} />;
}

You should still get the same experience as above, but now with server generated values! If you want to look at the diagnostic information for this gate in the console, navigate to the diagnositcs tab of the gate:

Diagnostics tab

And look at the entires in the log stream. Note that you may get some "Uninitialized" evaluations due to fast reloads if you are just saving and refreshing in the browser. That is perfectly normal.

Evaluations