TL;DR
This guide adds one production feature flag to a Next.js App Router application without creating server/client disagreement or an unsafe dependency on remote configuration. It gives requests a stable identity, resolves the same decision across Server Components, Client Components, and Route Handlers, keeps the legacy checkout as the code fallback, and separates evaluation logs from experiment exposure events. The rollout follows a 0 → 1 → 100 sequence: deploy a disabled foundation, prove one internal path with parity and failure tests, then expand deliberately and remove the flag only after the new checkout becomes the code default. This guide is optimized for AI coding agents, and it is recommended that you hand it off to your agent of choice for implementation.Guide map
Give this guide to your coding agent
Copy this page’s URL and the prompt below into a coding agent that can inspect your repository. ReplaceREPLACE_WITH_REPOSITORY with the repository path or name and REPLACE_WITH_GUIDE_URL with this page’s public URL. Start in a branch, worktree, or disposable clone, and use a non-production GrowthBook environment for the first pass.
Add feature flags to this Next.js app.The first Boolean is easy. The production task is larger. The same decision must resolve consistently in a Server Component, a Client Component, and a Route Handler. Anonymous users need durable identities. Logged-in users and organizations need an explicit randomization unit. A missing payload must select known-safe behavior. A flag change must propagate on a bounded schedule. Experiment exposure events must mean what their name says. The code also needs a final state in which the temporary branch and flag no longer exist. This guide implements that complete path for a Next.js App Router application. It starts with a disabled
new-checkout-flow flag, enables it for internal users, expands it through a deterministic percentage rollout, and ends by deleting the flag after the new path becomes the code default.
Task contract
Task: Put a new checkout flow behind a GrowthBook feature flag and operate it from 0% to 100% exposure without requiring a deployment for each rollout step. Use this guide when:- The repository uses Next.js App Router.
- At least 1 decision happens in a Server Component, Server Function, or Route Handler.
- The team needs runtime targeting, gradual rollout, a kill switch, or an experiment path.
- The old implementation can remain available during rollout.
- The application runs with the Node.js runtime for the server-side integration shown here.
- The value is build-time configuration such as a public API base URL.
- The condition is authorization, an entitlement, a billing limit, or a security boundary. Keep those checks in the authoritative server-side policy layer.
- The change cannot safely support 2 implementations at once, such as an incompatible destructive database migration without a compatibility phase.
- The application is a static export with no request-time server. Use a browser SDK integration instead and accept that the SDK payload and client-visible rules reach the browser.
- A single environment variable, changed only through a reviewed deployment, satisfies the full lifecycle.
flags 4.3.0, @flags-sdk/growthbook 0.3.1, and GrowthBook JavaScript/React SDK 1.7.0. Next.js 16 renamed middleware.ts to proxy.ts and made cookies() and headers() asynchronous. For Next.js 15, keep the same identity design but use the file convention and APIs supported by that version.
Required access:
- Read and edit access to the Next.js repository.
- A GrowthBook organization with permission to create an SDK Connection and a feature flag.
- Environment-variable access for local, preview, staging, and production deployments.
- Access to server logs or the application’s analytics pipeline.
- A non-production environment in which the new and old paths can both be exercised.
- The old checkout remains the code fallback until the rollout is complete.
- The same request identity receives the same decision in the page and API.
- A missing, disabled, malformed, timed-out, or unreachable GrowthBook payload selects the old checkout.
- Server logs distinguish feature evaluation from experiment exposure.
- Development, staging, and production use SDK keys tied to their own environments.
- CI rejects unknown feature keys and exercises both code paths.
- The team can turn the new path off, verify the rollback, and later remove both the old path and the flag.
false. Verify the page and the API return the legacy path for a known identity. Do not delete the flag during an incident. Deletion removes it from the SDK payload, which invokes the code fallback, but it also discards the clearest control-plane receipt while responders are diagnosing the problem.
First inspect the repository
Do not install an SDK until you know where the decision must run. Run these commands from the application root:- Is the repository App Router, Pages Router, or mixed?
- Is the checkout route rendered dynamically or prerendered?
- Which part of the decision belongs on the server?
- Does a Client Component only need the resolved value, or must it evaluate flags independently after hydration?
- What authenticated identity already exists: user, account, workspace, organization, or none?
- What anonymous identifier already exists, and who owns its consent and retention policy?
- Does an analytics event pipeline already reach the data source used for experimentation?
- Does the deployment run Node.js, Edge, static export, or a mixture?
- Is the application deployed on Vercel, self-hosted as multiple Next.js instances, or hosted on another platform?
- What is the safe behavior when configuration cannot load?
false the fallback. A coding agent should not infer an identity strategy, analytics contract, or safe failure state from a component name.
Repository decision tree
Follow this decision tree:@flags-sdk/growthbook. It fits App Router’s request model, provides a request-deduplicated identify function, supports Vercel Flags tooling, and keeps flag definitions in one typed module. The direct JavaScript and React SDK path appears later as a coherent alternative, not as code to mix into the same decision.
Choose the integration boundary
The packages overlap, but they solve different integration problems.
Use only 1 evaluation owner for a particular decision. If a Server Component evaluates
new-checkout-flow, pass that Boolean into the Client Component. Do not evaluate it again in the browser just because the component contains "use client". Re-evaluating can create a hydration mismatch, a different assignment, and a duplicate experiment exposure.
The GrowthBook Next.js adapter reference, JavaScript SDK reference, and React SDK reference cover each package separately. This guide covers the application architecture between them.
Create the GrowthBook environment boundary
Create separate SDK Connections for development, staging, and production. Each connection maps to 1 GrowthBook environment and has its own client key. The production process must receive only the production key. See GrowthBook environments and SDK connections. In GrowthBook:- Open SDK Configuration.
- Confirm the built-in
dev,staging, andproductionenvironments exist. - Create a Next.js/Flags SDK Connection for each environment used by the application.
- Copy each connection’s client key. It begins with
sdk-. - Keep the production connection server-side. Do not prefix these variables with
NEXT_PUBLIC_in the primary adapter path.
.env.local for local development:
FLAGS_SECRET as actual secrets. Never expose them through NEXT_PUBLIC_ variables or browser props.
Set the staging and production values in the deployment platform rather than committing them. Then verify the environment has a key without printing the key itself:
missing, stop. A silent empty key makes every feature fall back and can conceal a broken production integration.
Create a safe first feature
In GrowthBook, create a feature with these fields:dev. Leave staging and production at false until the code is deployed. In each environment, add rules in this order:
- Force
truewhenemployeeistrue. - Optionally force
truefor an explicit test organization. - Roll out
trueby percentage whenidentity_readyistrue. - Fall through to the feature default,
false.
- Use
organization_idwhen every member of a B2B account must receive the same checkout. - Use
user_idwhen assignments should follow authenticated users across devices. - Use
anonymous_idwhen the experience begins before login and must remain stable through that browser journey. - Use
idonly after defining exactly whatidrepresents in the application.
Implement the server-first adapter path
Install pinned major/minor versions for the first implementation. A lockfile should capture the exact transitive versions used by CI:Current adapter spelling
@flags-sdk/growthbook 0.3.1 exports createGrowthbookAdapter, with a lowercase b in book. Older examples use createGrowthBookAdapter. Pin the package and use the spelling exported by that version. If the import fails, inspect the installed package declaration rather than guessing.Establish an anonymous identity before rendering
Server Components can read cookies, but they cannot set a cookie during render. The first request therefore needs a boundary that creates the anonymous ID, forwards it to the current render, and sets it for later requests. In Next.js 16, createsrc/proxy.ts:
proxy.ts, merge this logic into it. Next.js permits only 1 Proxy file. If the application requires cookie consent before persistent analytics identifiers, do not set this cookie until consent is established. Use the application’s existing consent and identity system instead.
For Next.js 15 and earlier, use the supported middleware.ts convention. The first-request invariant stays the same: the value forwarded to the render and the value written to the cookie must be identical.
Verify the cookie without exposing it in application logs:
Define the authenticated viewer boundary
Do not make a feature SDK responsible for authentication. Createsrc/lib/auth/viewer.ts as the adapter between the application’s existing server-side session and feature attributes:
x-user-id or x-plan request headers. A caller can forge those headers unless a trusted ingress strips and replaces them.
For an Auth.js application, the replacement has this shape:
isEmployee from a trusted role or directory claim, not from an email suffix supplied by the browser.
Resolve one request-scoped attribute object
Createsrc/lib/feature-flags/attributes.ts:
dedupe ensures the request computes attributes once even if several flags call identify. All flag decisions in that request therefore see the same snapshot. The attribute object excludes email, access tokens, names, raw roles, and the session object.
The missing-identity sentinel is not a valid rollout population. Every percentage or experiment rule in this guide includes identity_ready = true. That condition prevents all requests with a broken Proxy or session boundary from sharing 1 deterministic bucket.
Add evaluation and exposure receipts
Feature evaluation and experiment exposure are different events:- A feature evaluation occurs whenever code asks for a feature value. A force rule and a percentage rollout do not create experiment exposures.
- An experiment exposure occurs when an experiment rule assigns a variation and invokes the SDK tracking callback.
src/lib/feature-flags/telemetry.ts:
hashValue, map it through a stable pseudonymization scheme that the outcome pipeline also uses.
Feature diagnostics can query feature-evaluation events after you configure a Feature Usage Query. The Feature Evaluation Diagnostics reference documents the required feature_key and timestamp fields and optional value, rule, and unit metadata.
Define typed flags in one server-only module
Createsrc/lib/feature-flags/flags.ts:
server-only import makes an accidental Client Component import fail at build time. The flag fallback is false, which points to code that already works. The adapter initializes lazily and evaluates with the request attributes returned by identify.
The 1,200 ms timeout limits cold-start waiting for a feature payload. It is not a universal latency target. Choose a value using your deployment region, observed CDN behavior, and application latency budget. The SDK can use a cached payload when one exists; on a first cold start with no usable payload, the feature returns the fallback.
Pass the server decision into the Client Component
Createsrc/app/checkout/checkout-client.tsx:
src/app/checkout/page.tsx:
identify reads cookies and headers, the route is request-dependent. Do not force this page to static rendering. If the surrounding layout uses Cache Components, keep the flag evaluation in the dynamic part of the tree and pass request values into cached functions rather than reading cookies inside a cached scope.
Use the same flag in a Route Handler
The backend must not assume the browser used a particular path. Evaluate the same server flag in the API boundary. Createsrc/app/api/checkout/config/route.ts:
Make identity an explicit product contract
Deterministic hashing only produces stable assignments when the hash input is stable. The SDK cannot repair an identity model that changes halfway through a workflow. The canonical code exposes 4 identifiers for different purposes:
Do not create an experiment rule until the team writes down its randomization unit. A checkout experiment that begins before authentication usually needs
anonymous_id. A seat-management change should usually use organization_id. A personal settings change may use user_id.
Handle anonymous-to-authenticated transitions deliberately
The example’sid changes at login. That is useful for authenticated targeting, but it can rebucket a user if an experiment hashes on id. Choose 1 of these patterns instead of accepting the transition accidentally:
- Hash on
anonymous_idfor the whole pre-login journey. Preserve the cookie through checkout. Join the eventual conversion to that anonymous ID in the event pipeline. - Hash on
user_idonly after authentication. Add a rule condition requiring a non-empty user ID, and keep anonymous users on the fallback. - Hash on
organization_idfor account-level behavior. Require a selected organization and ensure every backend call resolves the same organization from the trusted session. - Use sticky bucketing when the experiment requires assignment continuity across identifier or experiment changes. Sticky bucketing requires an implemented storage service and is a paid-plan capability in GrowthBook Cloud. It is not part of this guide’s free baseline.
Keep tenant selection consistent
Many B2B applications let one user switch organizations. Resolveorganization_id from the same server-side tenant context used for authorization. Do not read it from a query string and trust it as a targeting attribute.
For an organization rollout:
- Confirm the route’s authorized organization.
- Put that ID into
organization_id. - Configure the GrowthBook rollout to hash on
organization_id. - Add a condition that
organization_idis not empty. - Test 2 users in the same organization and 2 organizations with the same user.
Treat attribute changes as schema migrations
Defineanonymous_id, user_id, organization_id, employee, plan, and identity_ready under SDK Configuration → Attributes with matching data types. Attribute values are evaluated by the SDK; the attribute definitions in GrowthBook provide targeting metadata and authoring controls.
Before renaming or changing an attribute:
- Find every flag and experiment that references it.
- Find every SDK that sends it.
- Check case, empty-string, and missing-value behavior.
- Check the identifier selected by active experiments.
- Deploy code that sends both old and new attributes.
- Migrate rules.
- Wait through the active assignment and cache window.
- Remove the old attribute only after the old rules and SDKs are gone.
Understand what the adapter does at runtime
@flags-sdk/growthbook 0.3.1 creates a shared GrowthBookClient. The flag wrapper supplies request-specific attributes as a UserContext, so attributes do not need to mutate a global singleton. The adapter lazily initializes the client, refreshes its payload, evaluates the feature, and returns the flag’s defaultValue when the result is null.
This distinction matters:
- Feature definition delivery can involve I/O. The adapter must obtain and refresh the SDK payload from GrowthBook’s CDN or an optional Global Config store.
- Rule evaluation is local after the payload is available. The SDK evaluates targeting conditions and deterministic hashing in the process.
- Remote evaluation is a different mode. It sends attributes to a remote evaluator and is not the baseline in this guide.
- Experiment tracking is application I/O. Your callback sends an exposure to an analytics or warehouse pipeline; GrowthBook does not infer that event merely because a rule exists.
Use the default adapter when defaults are enough
The custom adapter in this guide validates required configuration and sets observability callbacks. A smaller application can use the default singleton:GROWTHBOOK_CLIENT_KEY, GROWTHBOOK_API_HOST, GROWTHBOOK_APP_ORIGIN, and optional Global Config variables. It logs a missing client key rather than enforcing the deployment check used above. Keep an independent environment-variable preflight if a missing key should fail the build or startup.
Do not use one mutable GrowthBook instance per user
An unsafe server implementation often looks like this:
GrowthBook class directly on the server, create a request-scoped instance and destroy it after extracting results and deferred tracking data.
Use the direct JavaScript and React SDK path when needed
Use this alternative when the repository does not use the Flags SDK convention, needs complete ownership of Next.js fetch caching, or already has a direct GrowthBook integration. Do not install it alongside the primary adapter for the same flag. The alternative below does 4 things:- Fetches the SDK payload with Next.js Data Cache tags.
- Creates a request-scoped server SDK instance.
- Evaluates server flags before rendering.
- Hydrates a decrypted payload and the same attributes for client-only flags.
Define strict feature types
Createsrc/lib/feature-flags/app-features.ts:
Fetch and initialize on the server
Createsrc/lib/feature-flags/direct-server.ts:
{apiHost}/api/features/{clientKey}. It is a read-only SDK Connection endpoint. Next.js caches successful responses for up to 300 seconds and associates them with growthbook-features. The fallback is an empty payload, which makes unknown features use their code fallbacks.
AbortSignal.timeout() requires the tested Node.js runtime. For an older runtime, use an AbortController timer. Do not omit the bounded failure path on a request-critical page.
Hydrate client-only features without flicker
Createsrc/lib/feature-flags/direct-provider.tsx:
initSync prevents the first client render from evaluating against an empty payload. It cannot decrypt encrypted payloads, so the server must pass getDecryptedPayload(). That is why this pattern is inappropriate when the payload itself must stay private.
The example pushes browser exposures to dataLayer. Confirm that Google Tag Manager, or another consumer of that array, sends the event to the data source queried by GrowthBook. If the application uses Segment, RudderStack, Snowplow, or a custom tracker, replace the callback with that tracker’s established event contract.
Create a client-only component, src/app/checkout/client-checkout-help.tsx:
src/app/checkout/page.tsx:
new-checkout-flow once. The browser evaluates only client-checkout-help. If both flags become experiments, their exposures come from different tracking callbacks by design.
Choose server or browser tracking, not both
For each experiment, document its evaluation owner:Design feature definition delivery
Feature evaluation correctness depends on which payload a process holds. Choose a delivery strategy instead of combining every cache layer.Adapter baseline: CDN plus SDK cache
Without Global Config, the adapter initializes itsGrowthBookClient from the SDK endpoint and uses the JavaScript SDK’s caching and stale-while-revalidate behavior. The adapter requests a refresh before evaluation. In a long-lived Node.js process, the in-memory cache can serve later requests. In serverless deployments, each warm isolate has its own memory and a new isolate can start without that cache.
Use this baseline when:
- A short propagation window is acceptable.
- The code fallback is safe on a cold start.
- The deployment does not require a shared configuration store.
- The team can observe payload-source failures.
Vercel adapter option: Global Config
@flags-sdk/growthbook 0.3.1 supports Vercel Global Config. Set these server variables:
- The webhook updates the item tied to the same SDK client key used by the app.
- The GrowthBook environment and Vercel deployment environment match.
- The config store size can hold the SDK payload.
- A missing or malformed item falls back safely.
- The Vercel API token used by the webhook has only the required scope and has a rotation owner.
Direct SDK option: Next.js Data Cache plus webhook
The direct implementation tags its payload fetch. Add a signed webhook Route Handler to invalidate that tag. Createsrc/app/api/internal/growthbook-webhook/route.ts:
GROWTHBOOK_WEBHOOK_SECRET to the shared secret shown for that SDK Webhook. In GrowthBook, configure an HTTP Endpoint SDK Webhook with this Route Handler’s HTTPS URL and test it. GrowthBook signs SDK webhooks and retries a failed delivery up to 2 additional times. Read the SDK Webhooks signature and payload reference before changing the verification code.
The handler verifies the raw request body before parsing it and rejects messages older than 5 minutes. Record processed webhook-id values in a durable store if duplicate processing would have side effects. Cache invalidation is idempotent, so an in-memory duplicate guard is not required here.
For self-hosted Next.js with multiple application instances, confirm the Data Cache and tag invalidation are shared. Default local caches do not automatically coordinate every instance. A webhook that reaches only 1 node can leave other nodes stale until their time-based revalidation. Use a shared cache handler, broadcast invalidation, or rely on a delivery layer designed for the deployment topology.
Streaming is optional, not the default answer
The direct JavaScript and React SDKs can subscribe to server-sent event updates when streaming is enabled. GrowthBook Cloud and GrowthBook Proxy support the documented streaming path. The Flags adapter explicitly initializes with streaming disabled in its current implementation. Use streaming only when the application genuinely needs faster propagation than a webhook or bounded cache window. Account for connection limits, reconnect behavior, serverless lifecycle, background tabs, and self-hosted proxy availability. A kill switch still needs a code fallback because a stream cannot help a process that never obtained a valid initial payload.Enforce feature keys and values with TypeScript
A fallback gives runtime safety. Strict feature types give change-time safety. Use both. The primary Flags adapter already declares the local flag asflag<boolean, AppAttributes> and growthbookAdapter.feature<boolean>(). A string value cannot reach the component without a TypeScript error. Centralizing exports also makes direct string references easy to find:
GrowthBook<AppFeatures> as shown above. Then a typo such as this fails compilation:
Generate types from GrowthBook
For a large feature set, generateAppFeatures rather than maintaining it manually. Install the current GrowthBook CLI and authenticate with a Secret Key or Personal Access Token that can read the intended organization:
new-checkout-flow is Boolean and confirm the command read the intended GrowthBook organization and project.
Add deterministic scripts to package.json:
GBCLI_BEARER_AUTH through the secret store. Do not put it in .env.example, a generated file, a test snapshot, or build logs. If CI cannot contact GrowthBook, choose 1 policy explicitly:
- Block merges because the generated feature contract is authoritative.
- Run generation in a scheduled job and make normal CI validate the checked-in result.
- Keep a manually reviewed local interface and skip live generation.
Validate the exact key inventory
Run this search in CI or during review:Test behavior without a live control plane
Tests should prove evaluation semantics with a fixed payload. They should not depend on whichever rule happens to be published in a shared development environment.Test the attribute builder
Createsrc/lib/feature-flags/attributes.test.ts:
Test rules with a fixed SDK payload
Createsrc/lib/feature-flags/evaluation.test.ts:
Test both components
The decision and the rendering branches are separate contracts. Use the repository’s React test runner to renderCheckoutClient with useNewFlow={false} and useNewFlow={true}. Assert that the legacy and new test IDs are mutually exclusive. If the repository has no component-test setup, the Playwright test below covers the deployed branch while the fixed SDK test covers both values.
Test page and API parity
Createtests/feature-flag-parity.spec.ts:
baseURL points at a production build under test. context.request shares cookies with that browser context. A standalone request fixture does not represent the same browser session, so do not use it for this parity check.
Run the verification sequence
Add or adapt these scripts:Observe decisions without corrupting experiments
An application needs 3 separate observability channels:- Payload health: Did initialization use a current payload, a cache, a timeout, or a fallback?
- Feature evaluation: Which feature, value, source, and rule did the SDK return?
- Experiment exposure: Which experiment assigned which variation to which randomization unit?
flag_used event.
Payload health
The direct path returnssource as next-cache-or-network or safe-fallback. Emit a counter for the fallback and alert on a sustained rate, not a single cold-start failure. The adapter’s underlying init() response can report network, cache, init, error, or timeout; if payload-source telemetry is operationally required, wrap initialization during process warm-up or use the direct path where that receipt is explicit.
Useful dimensions are deployment environment, application version, region, SDK client-key fingerprint, and payload dateUpdated. Do not log the full payload. It can contain internal flag names, rule structures, and variations.
Feature evaluation
UseonFeatureUsage for diagnostics and code-reference signals. Include:
Experiment exposure
An exposure event should fire only when an experiment rule assigns a variation at the point of exposure. The adapter’strackingCallback fires when an experiment-backed feature is evaluated. Structure the component tree so evaluation is close to the actual experience. Do not evaluate all known experiment flags in the root layout “just in case.” That overcounts users who never reach the feature.
At minimum, preserve:
Verify event delivery
For server logging, exercise a known experiment identity and search the logs:experiment_viewed should not appear for a force or percentage-rollout rule. It should appear only after you replace the rollout with an experiment rule and the identity is included.
In the warehouse, run a bounded query adapted to the application’s schema:
Secure the integration
Feature flags are operational controls, not security controls.Keep authorization outside the flag
This is unsafe:Minimize attributes
Send only fields used for targeting or assignment. Prefer opaque application IDs over email. If email-based targeting is unavoidable, configure a secure-string attribute and hash it exactly as documented in the JavaScript SDK secure attributes reference. Secure attribute hashing changes what appears in the SDK payload; it does not remove the need to control where the salt and raw values live. Keep attributes out of broad logs. The telemetry example pseudonymizes the unit ID when a telemetry key is configured. Review the retention period and access policy for that derived identifier.Decide whether the payload may reach the browser
Local browser evaluation exposes the SDK payload to the browser. Do not put secrets in feature values or targeting rules. Feature flags are configuration, not a secret manager. Use server evaluation when:- Rule conditions reveal internal account lists or unreleased product logic.
- Unused JSON/string variations contain sensitive configuration.
- The decision protects server behavior.
- A client could tamper with the rendered choice and the server must remain authoritative.
Separate credentials by purpose
Use distinct credentials:GROWTHBOOK_CLIENT_KEY: read-only SDK payload identity for 1 environment.GROWTHBOOK_DECRYPTION_KEY: decrypts encrypted SDK payloads; server-only in this guide.GROWTHBOOK_WEBHOOK_SECRET: verifies SDK webhook requests.GROWTHBOOK_API_KEY: optional read-only metadata access for Flags Explorer; never needed for normal evaluation.FLAGS_SECRET: protects the Vercel Flags discovery/override endpoint when that optional tooling is enabled.GBCLI_BEARER_AUTH: CLI credential for type generation or other reviewed automation.
Protect the optional Flags Explorer endpoint
Flags Explorer is not required for the implementation. If the team enables it, usecreateFlagsDiscoveryEndpoint, a read-only GrowthBook API credential, and FLAGS_SECRET as documented in the Next.js adapter Flags Explorer integration. Do not expose an unprotected route that lists internal flag metadata.
Know which controls require a plan
The baseline in this guide does not require an advanced release feature. A GrowthBook Cloud Starter organization can use built-in environments, Boolean flags, advanced targeting, manual percentage rollout, and an instant kill switch. The current Cloud Starter plan is limited by seats, projects, and included CDN usage even though feature flag and traffic counts are not priced per evaluation. Check the current GrowthBook pricing page before promising limits or availability. As verified on August 12, 2026:- Cloud Starter baseline: Default environments, unlimited feature flags, manual percentage rollouts, advanced targeting, kill switches, stale flag management, flag history, and standard webhooks.
- Cloud Pro additions relevant here: Scheduled feature flags, Safe Rollouts with automated rollback, sticky bucketing, code references, encrypted SDK endpoints, and remote evaluation.
- Cloud Enterprise additions relevant here: Ramp schedules, approval workflows, exportable audit logs, advanced access controls, and other governed release controls.
- Self-hosted: Open-source and commercial feature availability differs from GrowthBook Cloud. Verify the deployed license and server version rather than copying Cloud assumptions.
production, dev, staging, and test environments are available to free organizations. Custom environment packaging has changed over time, so confirm current plan details before creating per-branch or per-region environments.
The implementation must remain safe without plan-gated automation. If Safe Rollouts or ramp schedules are unavailable, use manual checkpoints with explicit stop criteria and a named operator.
Roll out from 0 to 1 to 100
A rollout is a sequence of verified state transitions, not a slider movement. Write the release record before exposing customers:Stage 0: Merge dark code
Before enabling the feature for anyone:- Create the flag with
falseas its default. - Keep production disabled.
- Deploy both implementations.
- Confirm a missing payload renders the legacy component and API path.
- Confirm the new code does not run hidden side effects while its UI is off.
- Run type, unit, build, and parity tests.
- Confirm server logs show
defaultValueor fallback behavior for a known request.
Stage 1: Enable internal identities
Publish the top-priority employee rule indev, then staging, then production. Use a trusted employee attribute. Verify:
- An employee sees the new component and API path.
- A non-employee with the same plan stays on the legacy path.
- The same identity receives the same value after navigation and refresh.
- The new path passes functional checkout tests.
- The kill switch returns the employee to the legacy path.
- Feature-evaluation receipts contain the intended
ruleId.
Stage 2: Enable a test organization
For B2B checkout, target 1 opt-in organization before percentage rollout. This reveals tenant-context errors that employee targeting may miss. Confirm that 2 users in the organization receive the same result and a user who switches to another organization receives that organization’s result. For a consumer application, use an explicit beta cohort or a small allowlist of durable IDs. Keep allowlists small; large ID lists expand the SDK payload and become hard to govern.Stage 3: Start a manual percentage rollout
Add a percentage rule below the internal and test-account rules:- Payload fallback and initialization errors.
- Checkout page and API parity.
- Server error and timeout rates by feature value.
- Payment or order-creation errors by feature value.
- Support reports and client exceptions.
- Assignment balance as a diagnostic, if the population is large enough.
- The count of missing identities.
Stage 4: Expand through checkpoints
A default manual schedule is:- Record the old and new coverage.
- Record who published it.
- Record the observation start and earliest next decision time.
- Verify the published payload reaches the application.
- Compare operational signals against predeclared stop thresholds.
- Exercise a known legacy and new identity.
- Either advance, hold, or roll back.
Stage 5: Reach 100% without deleting the fallback
At 100%, every eligible identity receives the new value, but the release is not finished. Keep the legacy path during a bounded rollback window. Verify:- All intended populations are eligible; empty identifiers are not silently falling through.
- Disabled environments still use the code fallback.
- A top-priority force-
falserule returns a test identity to legacy. - The production process has received the 100% payload in every relevant region.
- Background workers and Route Handlers that use the flag agree with the page.
- No active experiment still depends on the old variation.
Rollback: run the drill before it is needed
The safe rollback for this guide is a control-plane change, followed by an application receipt.Roll back a normal rollout
- Open
new-checkout-flowin GrowthBook. - Draft a production change that forces
falseabove every other rule, or disable the production feature environment. - Publish the change using the organization’s normal review policy.
- Wait no longer than the stated propagation objective.
- Request
/checkoutand/api/checkout/configwith the same known identity. - Confirm the page says
off, the API sayslegacy, and new checkouts use the old implementation. - Confirm error signals recover.
- Leave the rollback rule and incident evidence in place until the cause is understood.
Roll back when GrowthBook is unavailable
If the control plane is unavailable but the application has a cachedtrue payload, a code fallback alone does not override that cached value. Plan an application-level emergency mechanism for changes whose risk requires independence from the flag delivery plane. Options include:
- A server-only environment variable such as
FORCE_LEGACY_CHECKOUT=true, read before the SDK decision and changed through the deployment platform. - An operational configuration store already used for emergency controls.
- A deployment that changes the code default and bypasses the new path.
shouldUseNewCheckout() from both page and API instead of calling the raw flag. Document who can set the variable, how long a deployment change takes, and how the override is removed. Do not create a generic unsigned header that forces arbitrary flags.
Do not use rollback to reverse incompatible data
The legacy code path must remain compatible with state written by the new path. Use expand-and-contract migrations:- Deploy schema changes that both versions can read.
- Write backward-compatible data.
- Roll out the new behavior.
- Wait through the rollback window.
- Migrate or backfill data.
- Remove legacy reads only after rollback is no longer required.
Use the failure matrix during review
Review these cases before production:
The failure state must be visible. “Falls back safely” without a counter or log turns a broken integration into a long-lived silent release failure.
Know when DIY is the better answer
GrowthBook is not necessary for every Boolean. Keep a deployment-time environment variable when all of these are true:- The value changes only through a normal deployment.
- No user, account, attribute, or percentage targeting is required.
- The application has 1 environment or environment differences already map cleanly to deployment configuration.
- No non-developer needs to operate the value.
- No experiment will use the decision.
- No audit, approval, usage diagnostic, or stale-flag workflow is required.
- A code deployment is an acceptable rollback mechanism.
- The configuration is expected to remain permanent rather than become temporary release debt.
ENABLE_VERBOSE_LOCAL_LOGS in a developer-only build may be a valid environment variable. An entitlement such as CAN_EXPORT_BILLING_DATA should remain in the permission system, not GrowthBook.
The DIY threshold
A homegrown Boolean usually evolves through this sequence:if (enabled). The application still owns both branches. GrowthBook supplies a shared feature model, environment-specific payloads, targeting rules, deterministic assignment, an operator interface, integration points for experiments, and lifecycle metadata. The team still owns identity correctness, authorization, analytics delivery, application compatibility, incident response, and code removal.
Compare the maintenance obligation
Use this decision record:
Do not select GrowthBook only to avoid writing 1 conditional. Select it when the release and measurement lifecycle would otherwise become internal infrastructure.
Remove the flag after the release
The terminal state is not “100% on.” It is 1 implementation with no temporary decision.Establish removal criteria at creation
Record:Remove the losing branch in order
After the rollback window:- Confirm no active experiment, ramp, or incident depends on the flag.
- Confirm production has been stable at
truefor the agreed period. - Change code so the new checkout is unconditional.
- Remove the legacy component, API implementation, imports, tests, and telemetry dimensions used only by the split.
- Deploy the unconditional code while the GrowthBook flag still exists.
- Verify checkout behavior and health.
- Archive the GrowthBook feature.
- Wait through the maximum old-application deployment window.
- Delete the feature only if organizational retention policy allows it.
- Remove the key from generated types and fixtures.
Definition of done
The integration is complete only when every applicable statement is true.Architecture and identity
- The repository inspection identifies App Router, runtime, auth boundary, analytics path, hosting topology, and safe fallback.
- Server-owned decisions evaluate on the server and reach Client Components as resolved values.
- The page and Route Handler use the same request identity and organization context.
- The anonymous ID is durable, consent-compatible, and available on the first render.
- Percentage rules and experiments exclude missing identities.
- The randomization unit is documented and does not change mid-rollout.
Delivery and failure behavior
- Development, staging, and production use SDK Connections tied to the correct GrowthBook environments.
- No server credential is exposed through
NEXT_PUBLIC_variables or browser props. - Initialization has a bounded timeout and a known-safe
falsefallback. - The deployed cache strategy has a measured propagation objective.
- Webhook or Global Config delivery, when used, has a successful live receipt.
- Multi-instance cache invalidation is coordinated or explicitly bounded by time.
- An independent emergency rollback exists if the change’s risk requires it.
Tests and observability
- TypeScript rejects an unknown feature key and wrong feature value type.
- Unit tests cover employee targeting, missing identity, deterministic assignment, both rollout sides, and unknown-feature fallback.
- The production build succeeds.
- Playwright proves the page and API agree before and after refresh.
- Feature evaluation and experiment exposure use separate event contracts.
- An experiment exposure reaches the actual analytics or warehouse destination before an experiment starts.
- Logs and metrics can distinguish a current payload from a fallback or stale payload.
Operations and lifecycle
- Internal targeting succeeds before customer exposure.
- Every percentage checkpoint has named stop conditions and a release record.
- A rollback drill forces the legacy page and API path within the stated objective.
- Both code paths remain data-compatible through the rollback window.
- The feature has an owner, cleanup issue, and removal deadline.
- The final state removes the legacy branch, the flag check, fixtures, generated type, and feature after old deployments no longer depend on it.
Source map and freshness contract
This guide was verified on August 12, 2026 against GrowthBook commite44a15af063860c7118f52508746356d55e5a91d, the released package versions in tested_stack, and current Next.js 16 documentation.
Primary GrowthBook source paths used:
docs/docs/lib/nextjs.mdxfor the Flags adapter, identification, tracking, optional config store, and Flags Explorer behavior.docs/docs/lib/js.mdxfor initialization, caching, deferred tracking, remote evaluation, strict TypeScript, secure attributes, and feature usage.docs/docs/lib/react.mdxfor React Server Components, client hydration, and tracking patterns.packages/sdk-js/src/GrowthBookClient.tsandpackages/sdk-js/src/core.tsfor request-scoped user contexts, init results, evaluation callbacks, and cleanup behavior.packages/sdk-js/test/typed-features.test.tsandpackages/sdk-react/test/main.test.tsxfor strict typing and React integration test coverage.- Feature fundamentals, rules, targeting, environments, diagnostics, and SDK Webhooks for control-plane behavior and operator receipts.
- The released
@flags-sdk/growthbookadapter source for the currentcreateGrowthbookAdapterspelling, Global Config variables, refresh behavior, and request-scoped user context. - Vercel’s GrowthBook Flags SDK example for current Next.js 16 identification, server tracking, and client tracking glue.
- Next.js
cookies()behavior,proxy.tsconvention,after()lifecycle,fetchcache options, andrevalidateTagfor framework-specific behavior. - The current GrowthBook plan matrix for plan-gated feature labels. Pricing and packaging are volatile.

