← All posts

Add feature flags to your Next.js app in 5 minutes

November 6, 2026

A feature flag separates deploying code from releasing it. You ship a change turned off, turn it on when you're ready, and turn it off again in seconds if something goes wrong, all without a redeploy.

In this tutorial, you'll put a new checkout page behind a flag in a Next.js app, release it to one user, and then use the same flag as a kill switch.

You'll need:

  • A Next.js 15+ app using the App Router (this was tested on Next.js 16.4)
  • Node.js 18 or later
  • A flagr.dev account

1. Create the flag

In the flagr.dev dashboard:

  1. Create a project called shop.
  2. Create an environment called production, and copy its SDK key. It starts with sdk_live_.
  3. Create a flag with the key new-checkout. New flags start disabled.

2. Install the SDK

npm install @flagr-dev/sdk server-only

Add your key to .env.local:

FLAGR_SDK_KEY=sdk_live_...

Don't add a NEXT_PUBLIC_ prefix, because that would expose the key to the browser.

3. Create one shared client

The client connects once, receives all your flags, and keeps them in memory. Every check after that is a local lookup with no network call, so you only want one client per server process.

Create lib/flagr.ts:

import "server-only";
import { FlagrClient } from "@flagr-dev/sdk";

let clientPromise: Promise<FlagrClient> | undefined;

export function getFlagr(): Promise<FlagrClient> {
  clientPromise ??= new Promise((resolve) => {
    const client = new FlagrClient(process.env.FLAGR_SDK_KEY!);

    // If flags can't load in time, render anyway. isEnabled returns your defaults.
    const timeout = setTimeout(() => resolve(client), 2000);

    client.connect(() => {
      clearTimeout(timeout);
      resolve(client);
    });
  });

  return clientPromise;
}

The timeout matters. The connect callback only runs once flags have loaded, so without a timeout, a bad key or a network problem would leave your page waiting forever.

4. Check the flag in a page

Replace app/page.tsx:

import { Suspense } from "react";
import { connection } from "next/server";
import { getFlagr } from "@/lib/flagr";

async function Checkout({ searchParams }: { searchParams: Promise<{ user?: string }> }) {
  await connection(); // Evaluate on every request, not at build time

  const { user } = await searchParams;
  const flagr = await getFlagr();
  const showNewCheckout = flagr.isEnabled("new-checkout", user ?? "");

  return <h1>{showNewCheckout ? "New checkout" : "Current checkout"}</h1>;
}

export default function Home({ searchParams }: { searchParams: Promise<{ user?: string }> }) {
  return (
    <main>
      <Suspense fallback={<h1>Loading…</h1>}>
        <Checkout searchParams={searchParams} />
      </Suspense>
    </main>
  );
}

await connection() is the important line. Without it, Next.js could evaluate the flag once at build time and keep serving that result. It works whether or not your app has cacheComponents turned on.

The ?user= parameter keeps this demo simple. In a real app, use the ID from your session instead.

5. Release it

Build and start the app:

npm run build && npm start

Open http://localhost:3000. You'll see Current checkout.

In the dashboard, set new-checkout to Enabled and refresh the page. You'll see New checkout, with no rebuild or restart.

6. Release to one user first

Set the flag to Partially Enabled and add user-1 to its tenant list. Then open:

A tenant can be anything your product identifies: a user, an organization, or an account. When a check has no tenant ID, a partially enabled flag returns false.

7. Use it as a kill switch

Now imagine the new checkout causes an incident. Set the flag to Disabled, and everyone gets the old checkout on their next request.

Your tenant list is kept, so you can return to Partially Enabled after the fix and carry on from where you stopped.

Where to run this

This setup suits a long-running Node.js server, such as next start in a container or on a VM, because the client holds an open connection that receives flag changes.

On serverless platforms, instances can pause between requests, so changes may not arrive right away. Test this on your platform first. If you need a fresh answer per request, you can call the POST /evaluate endpoint instead, which adds a network call to each check.

Next steps

The flag service in this tutorial is flagr.dev. You can create an account and try it on the free Dev tier.