FEATUREFLOW V2AI-native · MCP-enabled ·Only 5 spots left at 50% off:NEXT50claim →
Continuous DeliveryAug 28, 2026

Feature Flags and SSR: Stop the Flash of the Wrong Variant

O
Oliver Oldfield-Hodge
Founder, Featureflow

The server rendered the new pricing page. Three hundred milliseconds later the browser repainted the old one. Your user watched both.

Server-side rendering gives you two flag evaluations for every page view: one on the server when the HTML is generated, and one in the browser when the client SDK boots. The server has flag config in memory and answers instantly. The browser starts cold — until its first config fetch completes, it has no flag state at all, so it falls back to defaults. If the server said on and the browser's default is off, the page visibly changes underneath the user.

In React the same disagreement has a second cost: the client's first render must match the server HTML, or hydration warns and re-renders. And your experiment data takes a hit too — an analytics event fired during the flicker window attributes the user to a variant they only saw for a frame.

One evaluation, two renderers

The fix is to stop letting the two sides evaluate independently. Evaluate once on the server with the server-side SDK, render the HTML from those answers, then hand the same answers to the browser client as its starting state. The Featureflow JavaScript client accepts a defaultFeatures map at init — features evaluate against it immediately, before any network round trip, and live config takes over once it streams in.

// Server — one singleton client, evaluate per request
import Featureflow from 'featureflow-node-sdk';

const featureflow = new Featureflow.Client({
  apiKey: process.env.FEATUREFLOW_SERVER_KEY,
});

app.get('/pricing', (req, res) => {
  const user = new Featureflow.UserBuilder(req.userId).build();
  const bootstrap = {
    'new-pricing-page': featureflow
      .evaluate('new-pricing-page', user)
      .value(),
  };
  res.render('app', { html: renderApp(bootstrap), bootstrap });
});

// Browser — first paint agrees with the server's HTML
const featureflow = Featureflow.init(FF_JS_CLIENT_KEY, user, {
  defaultFeatures: window.__FF_BOOTSTRAP__,
});

Two details make or break this. First, use the same user key on both sides — percentage rollouts bucket on the key, so a server that evaluates req.userId while the browser sends a device id can land the same human in different variants. Second, serialize only the flags the page actually renders. The bootstrap is view state, not a config dump.

When you can't bootstrap: hold the render

On a purely client-rendered page there is no server evaluation to inherit, but you can still refuse to paint the wrong thing. The Featureflow React client supports waitForInit with an optional preInitComponent — a skeleton until config arrives, instead of a confident render of the wrong variant. That trades flicker for a brief wait, which is the right trade below the fold and the wrong one on a landing page. Initial loads are cached and typically resolve in milliseconds, so the wait is shorter than the flicker it replaces.

Flicker isn't a rendering bug — it's two evaluators answering the same question at different times with different information. Give them the same information and the answers agree.

#FeatureFlags#SSR#React#NextJS#ContinuousDelivery

First paint, right variant

Start free with Featureflow — server and client SDKs that share one evaluation model, with bootstrap defaults and realtime streaming updates.

Start Now (Free)

Related Articles