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:
- Create a project called
shop. - Create an environment called
production, and copy its SDK key. It starts withsdk_live_. - 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:
- http://localhost:3000/?user=user-1, which shows New checkout
- http://localhost:3000/?user=user-2, which shows Current checkout
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
- SDK reference: SDKs for TypeScript, Python, Go, Kotlin/JVM, and PHP
- API reference: manage flags from scripts and CI
- OpenFeature setup: use the TypeScript SDK through the OpenFeature standard
The flag service in this tutorial is flagr.dev. You can create an account and try it on the free Dev tier.