Frontend Integration & Client-Side Rendering

The browser is the last mile for feature flags — and the most unforgiving one. Flags that reach the client must arrive before the first render, match what the server already painted, and never expose targeting logic the user could manipulate. This overview covers the full delivery chain: how flags are evaluated on the server, serialized into the HTML, handed to the client SDK, and kept in sync from that point forward. The companion Backend Evaluation & Server-Side SDKs guide covers the server half of that chain.

Client-side flag delivery overview Flags are evaluated on the server, serialized into a bootstrap payload in the HTML, used during hydration, and then kept fresh by the client SDK. Server evaluates flags with full context Bootstrap payload resolved variants inlined in HTML Hydration client SDK reads bootstrap snapshot after hydration Client SDK poll / stream for updates Control plane updated flag config streamed / polled
Flags are resolved server-side, inlined as a bootstrap payload, consumed synchronously during hydration, then kept current by the client SDK through polling or streaming.

Architecture Overview

The client flag delivery path has four distinct stages, and a failure at any one of them shows up immediately in the UI.

Server evaluation runs before the response leaves the origin. The server assembles a full evaluation context — user identity, plan tier, request locale — and resolves every flag the page depends on. This is the only point in the chain where targeting logic can access sensitive attributes safely.

Bootstrap payload is the serialized result of that evaluation, embedded in the HTML. It is a plain JSON object mapping flag keys to their resolved variants. The payload travels with the document so the client has the correct values the instant the parser reaches it — no network round-trip, no evaluation race.

Hydration is where the client SDK reads the bootstrap payload and initializes its local state from it, synchronously, before React (or Vue, or any other framework) reconciles the DOM. Initializing from the snapshot rather than from a fresh fetch eliminates the server-client divergence that causes hydration mismatches.

Client SDK updates begin after the page is interactive. The SDK polls or streams the control plane for flag changes and updates its local state reactively. User-level targeting that depends on browser-only attributes (viewport size, local timezone offset) can be evaluated here.

// Next.js App Router: server component evaluates, serializes, passes to client
import { OpenFeature } from '@openfeature/server-sdk';
import { FlagProvider } from './components/flag-provider';

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const client = OpenFeature.getClient('web');
  const ctx = { targetingKey: 'anon', plan: 'free' };

  // Resolve every flag the layout or its children need
  const bootstrapFlags = {
    'web.dashboard.new-nav': await client.getBooleanValue('web.dashboard.new-nav', false, ctx),
    'web.checkout.express-pay': await client.getBooleanValue('web.checkout.express-pay', false, ctx),
  };

  return (
    <html>
      <body>
        {/* FlagProvider inlines bootstrapFlags as a <script> and seeds the client SDK */}
        <FlagProvider bootstrap={bootstrapFlags}>{children}</FlagProvider>
      </body>
    </html>
  );
}

Lifecycle & Governance

Client-side flags have an abbreviated lifecycle compared to server-side ones, but the same governance requirements apply: every flag needs an owner, a creation date, and an expiry. Stale flags accumulate in the bootstrap payload and inflate every page response — 10 dead flags at 30 bytes each are invisible noise until a team has 200 of them. Lifecycle policies enforced at creation time (see designing a scalable flag taxonomy) prevent that drift.

Ecosystem Integration

The OpenFeature web SDK (@openfeature/web-sdk) is the canonical client implementation. It exposes the same getClient / getBooleanValue / getStringValue API as the server SDK, which means the same flag keys and evaluation logic work on both sides — removing the surface area for divergence.

In CI, validate that the bootstrap payload shape matches what the client SDK expects. A type mismatch between a server-generated string variant and a client-side boolean evaluation call is the kind of bug that slips through integration tests unless flag contracts are tested explicitly.

Progressive Delivery & Experimentation

Client-side flags support progressive delivery through percentage-based rollouts evaluated on the server (stable bucketing across replicas) or on the client (local to the session). Server-side bucketing is strongly preferred for experiment integrity: deterministic assignment survives page reloads and cross-device sessions, while client-side bucketing can shift when local storage is cleared. A/B tests on UI components should record the exposure event alongside the flag key and variant before the user interacts — not on conversion — to avoid selection bias from users who never see the treatment.

Operational Safety

A client-side flag that blocks render is an outage. Every flag read must have a hard-coded safe default, and the SDK initialization must not gate DOMContentLoaded. If the flag provider fails to connect, the page renders its defaults; it does not hang. UI flicker prevention and the bootstrap-payload pattern together address the most common form of client-side flag failure: the flash of the wrong variant between server render and client evaluation.

Secure browser delivery matters because the payload is visible to every user. Strip internal flag keys, targeting rule details, and any attribute that amounts to PII before the bootstrap JSON leaves the server. CSP boundaries govern how that inlined payload is permitted to execute, and edge and CDN delivery can pre-evaluate and cache the bootstrap payload closer to the user.

Compliance & Audit

Flag exposure events from the client must flow back to the same audit pipeline as server evaluations. Each event should carry the flag key, resolved variant, targeting key, and a session or request identifier. Stripping PII before the event leaves the browser is non-negotiable for GDPR-scoped deployments — the event consumer does not need the raw email address to attribute an exposure correctly.

Key Concepts at a Glance

Troubleshooting & FAQ

Why does the page flash the wrong variant on load?

The SDK is not initialized from the bootstrap payload before render. The component reads the default value synchronously, then the SDK connects asynchronously and the value changes, causing a visible flip. Fix: seed the client SDK from the server-evaluated snapshot before any component evaluates a flag.

Why does React throw a hydration mismatch error on flag-gated components?

The server rendered variant A (from the server-side evaluation), but the client re-evaluated and got variant B before React hydrated. The mismatch triggers a full re-render and a console warning. Fix: initialize the client SDK from the same snapshot the server used, so the first client-side read returns the same variant.

Can I evaluate flags on the client without a server-side bootstrap?

Yes, but only for purely presentational toggles with no SSR dependencies. The SDK initializes asynchronously, so you must render a loading or skeleton state until the provider is ready. Never do this for flags that gate content the server already rendered — the mismatch is guaranteed.

How many flags can I safely include in the bootstrap payload?

There is no hard limit, but every flag in the payload adds bytes to every page response. Scope the bootstrap to flags the page actually reads. A payload of 20–50 resolved boolean or string variants is typical; anything larger suggests the page is doing too much or flags are not being retired.