Nothing to install from here. The Flags SDK is
framework-agnostic at its core, and its Next integration — flags/next,
the one with flag(), dedupe() and the adapters — needs one thing from
Next: next/headers, for headers() and cookies(). Those exist here
under the same names and with the same shapes, one object per request,
which is what the SDK’s per-request dedupe keys on. The build answers
next/headers with them, so the SDK runs as written:
bun add flagsimport { flag, dedupe } from 'flags/next'
const visitor = dedupe(async ({ cookies, headers }) => ({
id: cookies.get('visitor')?.value ?? 'anonymous',
country: headers.get('x-vercel-ip-country') ?? headers.get('cf-ipcountry') ?? '',
}))
export const showBanner = flag<boolean, { id: string; country: string }>({
key: 'show-banner',
identify: visitor,
decide: ({ entities }) => entities?.country === 'GB',
})import { showBanner } from '../flags'
export default async function Home() {
const banner = await showBanner()
return banner ? <Banner /> : null
}A flag reads the request, so the page that awaits one renders per visitor —
the build says headers() in run, cookies() in run stream per request and
stores the rest as a shell, given a <Suspense> or a loading.tsx above
the read. That is the same line any headers() call earns; see
Static generation. An adapter — Statsig,
LaunchDarkly, Vercel’s own — is the SDK’s, configured the way its docs say.
The discovery endpoint is a route handler in the shape a route.ts already
has:
import { createFlagsDiscoveryEndpoint, getProviderData } from 'flags/next'
import * as flags from '../../../../flags'
export const GET = createFlagsDiscoveryEndpoint(async () => getProviderData(flags))
export const openapi = falseA test is not built, so it needs the alias too: one line in the test
preload, which the scaffold ships. Without it flags/next finds Next’s own
next/headers — installed by some other app in a monorepo — and throws that
headers was called outside a request scope.
mock.module('next/headers', () => import('@rsc-kit/core/request'))A flag then evaluates inside withRequest(), the way anything that reads the
request does in a test; see Testing.
What does not carry over is precompute(): it is built on a Next
middleware rewriting the url to a permutation, which is Next’s routing.
Read the flag in the page instead; a flag read under a boundary costs one
streamed hole, not the page.