Skip to main content

GrowthBook and Next.js

This document is a guide on how to add GrowthBook feature flags to your Next.js application. It assumes you are starting from scratch, so if you already have a Next.js application, you can skip to step 2.

Here is a video version similar to the steps below:

1. Create your Next.js App

Let's start by getting a basic Next.js app running:

yarn create next-app

Then cd into the newly create directory and run:

cd my-app
yarn dev -p 4000

Note: Both GrowthBook and Next.js run on port 3000 by default, so we're making our Next.js app use 4000 instead to avoid conflicts.

Visit http://localhost:4000 and you should see the application running!

2. GrowthBook Account

You will need a GrowthBook account. You can either run GrowthBook locally or using the cloud hosted GrowthBook at https://app.growthbook.io. If you are using the GrowthBook cloud, you can skip to step 3. If you are installing it locally, here is the quick start instructions - or you can follow the self hosting instructions.

git clone https://github.com/growthbook/growthbook.git
cd growthbook
docker-compose up -d

After that, visit http://localhost:3000 and create your first user account.

GrowthBook Signup Screen

3. Integrate the GrowthBook React SDK into our Next.js app

GrowthBook will generate some integration code for you, including a unique SDK Endpoint to load your features from.

GrowthBook Integration Code

We first need to install the GrowthBook React SDK in our Next.js app:

yarn add @growthbook/growthbook-react

Then we can modify the generated React code to work with the Next.js framework. Modify the file pages/_app.js with the following contents:

import "../styles/globals.css";
import { GrowthBook, GrowthBookProvider } from "@growthbook/growthbook-react";
import { useEffect } from "react";

const FEATURES_ENDPOINT = "http://localhost:3100/api/features/key_abc123";

// Create a GrowthBook instance
const growthbook = new GrowthBook({
trackingCallback: (experiment, result) => {
console.log("Viewed Experiment", experiment, result);
},
});

export default function MyApp({ Component, pageProps, router }) {
// Refresh features and targeting attributes on navigation
useEffect(() => {
fetch(FEATURES_ENDPOINT)
.then((res) => res.json())
.then((json) => {
growthbook.setFeatures(json.features);
});

growthbook.setAttributes({
id: "123",
loggedIn: true,
deviceId: "abcdef123456",
employee: true,
company: "acme",
country: "US",
browser: navigator.userAgent,
url: router.pathname,
});
}, [router.pathname]);

return (
<GrowthBookProvider growthbook={growthbook}>
<Component {...pageProps} />
</GrowthBookProvider>
);
}

Replace the FEATURES_ENDPOINT constant with the one you see in your instructions modal in GrowthBook.

In a real application, you would pull some of the targeting attributes from your authentication system or an API, but let's just leave them hard-coded for now.

4. Create a Feature in GrowthBook

Back in the GrowthBook application, we can create a new feature. For this tutorial, we'll make a simple on/off feature flag that determines whether or not we show a welcome banner on our site.

GrowthBook Create Feature

The key we chose (welcome-message) is what we will reference when using the GrowthBook SDK.

We can now edit pages/index.js and conditionally render a welcome message based on the state of the feature:

Add an import statement:

import { IfFeatureEnabled } from "@growthbook/growthbook-react";

And then put your welcome message somewhere on the page:

<IfFeatureEnabled feature="welcome-message">
<p>I hope you enjoy this site and have a great day!</p>
</IfFeatureEnabled>

If you refresh your Next.js app, you'll notice the welcome message is not rendered. This is because when creating the feature, we set the default value to off. At this point, we could safely deploy our change to production and not worry about breaking anything.

5. Turn on the feature for your team

Now we can add rules to the feature to turn it on for specific groups of users.

In the hard-coded targeting attributes we set in pages/_app.js, we designated ourselves as an internal employee. Let's use this attribute to turn on the feature for the whole internal team:

GrowthBook Targeting Rule

Refresh your Next.js app and you should now see the welcome message appearing! (Note: it might take up to 30 seconds for the API cache to refresh).

Next.js app with feature

If you change employee to false in pages/_app.js, you should see the welcome message disappear.

The best part about targeting attributes in GrowthBook is that they are evaluated entirely locally. Sensitive user data is never sent over the network and there is no performance penalty. Some other libraries require an HTTP request to evaluate a feature for a user and this is often a deal breaker.

6. Gradually roll out to your users

After you test the new feature within your team, you probably want to start rolling it out to real users.

We can do that with another rule in GrowthBook:

GrowthBook Rollout Rule

In the targeting attributes in pages/_app.js, make sure employee is set to false. That will ensure you skip the first rule we made and fall into the second rollout rule.

note

The GrowthBook SDK uses deterministic hashing to figure out whether or not someone is included in a rollout (or A/B test). The SDKs hash together the selected targeting attribute (id) and the feature key (welcome-message) and coverts it to a float between 0 and 1. If that float is less than or equal to the rollout percent, the user is included. This ensures a consistent UX and prevents one user from constantly switching between ON and OFF as they navigate your app.

Try changing the user id in the targeting attributes in pages/_app.js to a few different random strings and see what happens. You should notice about half of the time the welcome message shows up and half of the time it doesn't.

Conclusion and Next Steps

We showed here how to do a targeted rule, and how to do a rollout rule. It's also just as easy to make an A/B test in the same manner. You will need to set up an event tracking and connect GrowthBook to your data source.

You can look at the GrowthBook React SDK docs for more ways to use feature flags in your code besides the <IfFeatureEnabled> component. Once you do the initial integration work, it only takes a few seconds to wrap your code in feature flags. Once you realize how easy and stress-free deploys and experimentation can be, there's no going back.

GrowthBook on Next.js App Router

Next.js 13 introduces a new paradigm in Next.js apps that delineate between server-side and client-side components. Among these changes include a new folder layout for the default Next.js app.

The following examples illustrate how to use Growthbook using both server and client components on a brand new Next app created using create-next-app:

Server components

For Server components, you'll need to use the Growthbook JS SDK.

Changes to app/page.tsx:

import { GrowthBook } from "@growthbook/growthbook";

const growthbook = new GrowthBook({
apiHost: "...",
clientKey: "...",
enableDevMode: true,
trackingCallback: (experiment, result) => {
// TODO: Use your real analytics tracking system
console.log("Viewed Experiment", {
experimentId: experiment.key,
variationId: result.key,
});
},
});

export default async function Home() {
await growthbook.loadFeatures();

const isWelcomeBannerOn = growthbook.isOn("welcome-banner-01");

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
{isWelcomeBannerOn && <h1 className="text-4xl">Welcome!</h1>}

Note: The above approach can be swapped out with the fetch-based method illustrated in some other parts of our documentation.

Client components

New client component: WelcomeBanner.tsx

"use client";

import {
GrowthBook,
GrowthBookProvider,
useFeatureIsOn,
} from "@growthbook/growthbook-react";
import { useEffect } from "react";

const growthbook = new GrowthBook({
apiHost: "http://localhost:3100",
clientKey: "sdk-DxCjpOJmwJaWck",
enableDevMode: true,
trackingCallback: (experiment, result) => {
// TODO: Use your real analytics tracking system
console.log("Viewed Experiment", {
experimentId: experiment.key,
variationId: result.key,
});
},
});

const WelcomeBanner = () => {
const enabled = useFeatureIsOn("welcome-banner");
if (!enabled) return null;
return <h1 className="text-4xl">Welcome!</h1>;
};

const WelcomeBannerWithGB = () => {
useEffect(() => {
// Load features asynchronously when the app renders
growthbook.loadFeatures();
}, []);

return (
<GrowthBookProvider growthbook={growthbook}>
<WelcomeBanner />
</GrowthBookProvider>
);
};

export default WelcomeBannerWithGB;

Changes to app/pages.tsx

import WelcomeBanner from "./WelcomeBanner";

export default async function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<WelcomeBanner />
{/* ... */}
);
}