Skip to main content

Next.js Setup with Eppo Feature Flags

This guide walks through how to setup an Eppo Feature Flag on a client and server rendered component in a Next.js app. More specifically, this guide walks through the set up and settings of a sample Next.js app.

Architecture & support for hybrid rendering

Eppo offers the ability to use both client and server side rendering. Assignment can happen on either platform and the client SDK can be initialized with a configuration from your server SDK or from the Eppo CDN.

Loading flag configuration from CDN

Architecture: Loading flag configuration from CDN

Using Eppo's CDN is the easiest way to get started. Flag configuration is cached at the edge and the architecture requires no additional development beyond integrating the SDK.

See sample code below for how to instantiate these SDKs.

Loading flag configuration from server SDK

Architecture: Loading flag configuration from server SDK

In some cases, you may want to load flag configuration from your server SDK. Serialize and transfer the configuration to the client SDK.

// Client
import { offlineInit, Flag, ObfuscatedFlag } from "@eppo/js-client-sdk";

// configuration from your server SDK
const configurationJsonString: string = getConfigurationFromServer();
// The configuration will be not-obfuscated from your server SDK. If you have obfuscated flag values, you can use the `ObfuscatedFlag` type.
const flagsConfiguration: Record<string, Flag | ObfuscatedFlag> = JSON.parse(configurationJsonString);

offlineInit({
flagsConfiguration,
// If you have obfuscated flag values, you can use the `ObfuscatedFlag` type.
isObfuscated: true,
});

This function is synchronous and ready to handle assignments after it returns.

Additional details are available in the offline initialization documentation for the client SDK.

Start a new Next.js React App

Create the Next.js app

npx create-next-app

Provide the following answers when prompted:

  • What is your project named? react-sdk-walkthrough
  • Would you like to use TypeScript? Yes
  • Would you like to use ESLint? Yes
  • Would you like to use Tailwind CSS? No
  • Would you like to use src/ directory? No
  • Would you like to use App Router? (recommended) Yes
  • Would you like to customize the default import alias (@/*)? No

Run the Next.js app

cd react-sdk-walkthrough
yarn dev

Open a browser to the location output for local (e.g., http://localhost:3000)

Eppo flag setup

  1. Create and copy an SDK key in your Eppo app if you don't already have one.
  2. Create a feature flag in your Eppo app.
  3. Make sure your flag is turned on in the environment that correlates to your SDK key.

Client rendered component setup

Install the Eppo JS client SDK

yarn add @eppo/js-client-sdk

Create a randomization provider

In this section, we will create a provider based off our Usage in React documentation.

  1. Create a new /app file called EppoRandomizationProvider.tsx
  2. Copy and paste the following example code into EppoRandomizationProvider.tsx:
import { ReactElement, useEffect, useState } from 'react';

import { init } from '@eppo/js-client-sdk';

interface IEppoRandomizationProvider {
waitForInitialization?: boolean;
children: ReactElement;
loadingComponent?: ReactElement;
}

const EppoRandomizationProvider = ({
waitForInitialization = true,
children,
loadingComponent = <div>Loading...</div>,
}: IEppoRandomizationProvider): ReactElement => {
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
init({
apiKey: '<SDK-KEY>',
assignmentLogger: {
logAssignment(assignment) {
console.log('TODO: log', assignment);
},
},
}).then(() => {
return setIsInitialized(true);
});
}, []);

if (!waitForInitialization || isInitialized) {
return children;
}
return loadingComponent;
};

export default EppoRandomizationProvider;
  1. Replace <SDK-KEY> placeholder with your SDK key

Create a component to use the randomization provider

  1. Create a new /app file OfferExperiment.tsx
  2. Copy and paste the following code into the OfferExperiment.tsx file.
import { useMemo } from "react";
import { getInstance } from "@eppo/js-client-sdk";

const OfferComponent = () => {

const subjectKey = "<SUBJECT-KEY>"

const assignedVariation = useMemo(() => {
const eppoClient = getInstance();
return eppoClient.getStringAssignment("<FLAG-KEY>", subjectKey, <SUBJECT-ATTRIBUTES>, "<DEFAULT-VALUE>");
}, []);

return (
<div>
{assignedVariation === "control" && <p>50% off widgets!</p>}
{assignedVariation === "test" && <p>Buy one widget get one free!</p>}
{!assignedVariation && <p>Buy a widget today</p>}
</div>
);
}

export default OfferComponent;

For the sake of demonstration purposes, there is a display other than control for the disabled/error case which is the "Buy a widget today!" text. In most implementations, we’d default to control in that situation like so:

<h1>
{(!assignedVariation || assignedVariation === 'control') && (
<p>50% Off widgets!</p>
)}
{assignedVariation === 'test' && <p>Buy one widget, get one free!</p>}
</h1>
  1. Copy and paste your flag key into the getStringAssignment() method, along with a user id, user attributes, and default variation value.

Use the component in the application

  1. Put the offer component under the logo in page.tsx

Below is a snippet from what you will see in your page.tsx and where you should place the offer component:

<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>

// Offer component will go here
  1. Include the following line at the top of page.tsx to tell the Next.js app to treat the page as a client component (e.g., it is not server-side rendered, and can use react hooks)
'use client'; // Mark this file as a Client Component
  1. Import our offer experiment component
import OfferExperiment from './OfferExperiment';
  1. Add it to the page
<div className={styles.center}>
<EppoRandomizationProvider>
<OfferExperiment />
</EppoRandomizationProvider>
</div>

Since the experiment is off, we should get the default experience

&#39;Buy a widget&#39; experience

  1. Turn the flag on in Eppo

Flag switch Confirm flag updates

  1. Change the subject key to see the other experience
const subjectKey = 'TODO-A';

Variation experience with &#39;50% off widgets&#39;

Server rendered component setup

Install Eppo Node SDK

Follow the instructions to create your Next.js app as outlined in the previous section. For server-rendered components, we'll also need to install Eppo's Node.js SDK.

yarn add @eppo/node-server-sdk

The following assumes that you chose to use App Router when generating your Next.js app.

Would you like to use App Router? (recommended) Yes

SDK initialization

Create a new app/shared/get-eppo-client.ts file to handling SDK initialization. This file will reside in a new app/shared folder that will contain helpers for server-rendered components. Copy the code snippet below. The init function is called outside of the getEppoClient scope so that it's only called once.

'use server'; // directive needed for server-side rendering

import { init } from '@eppo/node-server-sdk';

const eppoClientPromise = init({
apiKey: '<SDK-KEY>', // SDK keys defined at https://eppo.cloud/feature-flags/keys
assignmentLogger: {
logAssignment(assignment) {
console.log('TODO: log ', assignment);
},
},
}).catch((err) => {
console.error('Error initializing Eppo SDK:', err);
});

export default async function getEppoClient() {
return await eppoClientPromise;
}

String assignment helper

Let's also create a helper function for fetching string assignments. This helper will ensure that the Eppo SDK is initialized before returning a value. To do this, create a new app/shared/get-string-assignment.ts file, and add the code snippet below.

'use server'; // directive needed for server-side rendering

import getEppoClient from './get-eppo-client';

export default async function getStringAssignment(
flagKey: string,
subjectKey: string,
subjectAttributes: Record<string, string> = {},
defaultValue: string = ''
): Promise<string> {
const eppoClient = await getEppoClient();
return (
eppoClient?.getStringAssignment(
flagKey,
subjectKey,
subjectAttributes,
defaultValue
) ?? defaultValue
);
}

Rendering the flag assignment

Next, we'll need to create a component that uses our flag variation. Create a new app/OfferExperement.tsx file, and add the snippet below. We’ll pass the assigned flag variation to the component, rather than fetching the assigned variation within the component.

import type { FunctionComponent } from 'react';

interface OfferExperimentProps {
variation: string;
}

export const OfferExperiment: FunctionComponent<OfferExperimentProps> = ({
variation,
}) => (
<h1>
{variation === 'control' && <p>50% Off widgets!</p>}
{variation === 'test' && <p>Buy one widget, get one free!</p>}
{!variation && <p>Buy a widget today!</p>}
</h1>
);

export default OfferExperiment;

Putting it all together

In app/page.tsx, remove any 'use client' directive if there is one. Then, add the logic that will retrieve the flag assignment and render our OfferExperiment component. Our app/page.tsx file should look similar to the following example.

import styles from './page.module.css';
import OfferExperiment from './OfferExperiment';
import getStringAssignment from './shared/get-string-assignment';

export default async function Home() {
const flagKey = '<FLAG-KEY>'; // defined in your flag configuration (https://eppo.cloud/feature-flags)
const subjectKey = '<SUBJECT-KEY>'; // ideally populated from something like login context
const offerVariation = await getStringAssignment(flagKey, subjectKey);
return (
<main className={styles.main}>
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>app/page.tsx</code>
</p>
<div className={styles.center}>
<OfferExperiment variation={offerVariation} />
</div>
{/* (other components) */}
</div>
{/* (other components) */}
</main>
);
}

If your development server is not yet running, run yarn dev. You should now see your experiment running with the new server rendered component.

Server rendered experiment