Feature Flags in React Apps: Rollouts ohne Redeploy
AI generated
</>
{ }
React · Feature Flags · Release Management
Feature Flags in React Apps
kontrollierte Rollouts ohne Redeploy

Feature Flags in React Apps entkoppeln die Frage, wann Code deployt wird, von der Frage, wann eine Funktion tatsächlich sichtbar ist. Statt riskanter Big-Bang-Releases lassen sich neue Funktionen schrittweise, für einzelne Nutzergruppen oder sofort per Kill-Switch wieder abschaltbar ausrollen, ganz ohne neuen Build.

17 Min. Lesezeit Context · LaunchDarkly · Unleash Progressive Rollouts · Testing

1. Warum Feature Flags Deployment und Release entkoppeln

Feature Flags in React Apps lösen ein Problem, das in klassischen Deployment-Workflows regelmäßig zu Stress führt: Code wird deployt, sobald er fertig ist, aber eine Funktion soll erst später, für bestimmte Nutzergruppen oder überhaupt nur testweise sichtbar werden. Ohne Feature Flags bleibt oft nur die Wahl zwischen langlebigen Feature-Branches mit schmerzhaften Merge-Konflikten oder riskanten Big-Bang-Releases, bei denen alles gleichzeitig live geht.

Mit Feature Flags in React Apps wird neuer Code kontinuierlich in den Hauptbranch integriert und regelmäßig deployt, bleibt aber hinter einem Schalter versteckt, bis das Produktteam die Funktion tatsächlich für Nutzer freigeben will. Diese Trennung von Deployment und Release ist der eigentliche Kern jeder Feature-Flag-Strategie und reduziert das Risiko jedes einzelnen Deployments erheblich, weil ein Rollback lediglich das Umlegen eines Schalters bedeutet, nicht das Zurückrollen eines ganzen Releases.

Für React-Anwendungen kommen zwei Wege infrage: ein selbst gebauter, leichtgewichtiger Context für einfache Anwendungsfälle, oder ein Managed Service wie LaunchDarkly, Unleash oder Flagsmith für komplexere Regeln mit Zielgruppen-Segmentierung. Die folgenden Abschnitte zeigen beide Wege mit konkretem Code.

2. Einen minimalen Feature-Flag-Context selbst bauen

Für viele Projekte reicht ein selbst gebauter Feature-Flag-Mechanismus vollkommen aus, insbesondere wenn nur wenige Flags gleichzeitig aktiv sind und keine komplexe Zielgruppen-Logik nötig ist. Die Grundidee: ein React Context hält ein Objekt mit Flag-Namen und booleschen Werten, das beim App-Start aus einer API oder statischen Konfiguration geladen wird.


// src/feature-flags/FeatureFlagProvider.tsx
import { createContext, useEffect, useState, type ReactNode } from "react";

type FlagSet = Record<string, boolean>;

interface FeatureFlagContextValue {
  flags: FlagSet;
  isLoading: boolean;
}

export const FeatureFlagContext = createContext<FeatureFlagContextValue>({
  flags: {},
  isLoading: true,
});

export function FeatureFlagProvider({ children }: { children: ReactNode }) {
  const [flags, setFlags] = useState<FlagSet>({});
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;

    fetch("/api/feature-flags")
      .then((res) => res.json())
      .then((data: FlagSet) => {
        if (!cancelled) {
          setFlags(data);
          setIsLoading(false);
        }
      })
      .catch(() => {
        // Fail closed: on error, treat all flags as disabled
        if (!cancelled) setIsLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, []);

  return (
    <FeatureFlagContext.Provider value={{ flags, isLoading }}>
      {children}
    </FeatureFlagContext.Provider>
  );
}

Diese Basisversion von Feature Flags in React lädt einmalig beim Start der Anwendung alle relevanten Flags von einer eigenen API-Route, die serverseitig Regeln wie Nutzerrolle oder Umgebung auswerten kann. Der Fail-Closed-Ansatz im catch-Block ist bewusst gewählt: Wenn das Laden der Flags fehlschlägt, sollen neue, potenziell unfertige Funktionen deaktiviert bleiben, nicht versehentlich für alle sichtbar werden.

3. Feature Flags mit useContext sauber konsumieren

Der Konsum von Feature Flags in React-Komponenten sollte über einen eigenen Hook laufen, statt den Context direkt zu importieren. Das kapselt die Logik, erlaubt später einen Wechsel des zugrunde liegenden Mechanismus, ohne jede Komponente anzufassen, und macht Tippfehler in Flag-Namen durch TypeScript-Autovervollständigung unwahrscheinlicher.


// src/feature-flags/useFeatureFlag.ts
import { useContext } from "react";
import { FeatureFlagContext } from "./FeatureFlagProvider";

type FlagName = "new-checkout-flow" | "dark-mode" | "ai-search-suggestions";

export function useFeatureFlag(name: FlagName): boolean {
  const { flags } = useContext(FeatureFlagContext);
  return flags[name] ?? false;
}

// Usage inside a component
function CheckoutPage() {
  const useNewCheckout = useFeatureFlag("new-checkout-flow");

  return useNewCheckout ? <NewCheckoutFlow /> : <LegacyCheckoutFlow />;
}

Der Typ FlagName als String Union sorgt dafür, dass beim Aufruf von useFeatureFlag nur bekannte Flag-Namen erlaubt sind, ein Tippfehler wird sofort vom TypeScript-Compiler erkannt statt erst zur Laufzeit unbemerkt als false durchzugehen. Für Feature Flags in React-Projekten mit vielen Flags lohnt sich diese zentrale Typdefinition, weil sie gleichzeitig als Dokumentation aller aktiven Flags dient.

4. Managed Services: LaunchDarkly, Unleash und Flagsmith

Sobald Feature Flags in React Apps komplexere Anforderungen erfüllen müssen, etwa Zielgruppen-Segmentierung nach Land oder Abonnement-Typ, Echtzeit-Updates ohne Neuladen der Seite oder ein Audit-Log für Compliance-Zwecke, lohnt sich ein Managed Service. LaunchDarkly gilt als Marktführer mit ausgereiftem Dashboard und SDK für praktisch jede Sprache, ist dafür aber auch das teuerste Angebot der drei Optionen.

Unleash ist Open Source und kann komplett selbst gehostet werden, was besonders für Teams mit strengen Datenschutzanforderungen relevant ist. Flagsmith positioniert sich preislich zwischen beiden und bietet ebenfalls eine Self-Hosted-Variante an. Alle drei Services stellen ein React SDK bereit, das den eigenen Context-Ansatz aus dem vorherigen Abschnitt im Wesentlichen ersetzt, aber um Echtzeit-Updates über Server-Sent Events oder WebSockets erweitert.


// Example using Unleash's official React SDK
import { FlagProvider, useFlag, useVariant } from "@unleash/proxy-client-react";

const unleashConfig = {
  url: "https://unleash.mironsoft.de/api/frontend",
  clientKey: import.meta.env.VITE_UNLEASH_CLIENT_KEY,
  appName: "shop-frontend",
};

function App() {
  return (
    <FlagProvider config={unleashConfig}>
      <CheckoutPage />
    </FlagProvider>
  );
}

function CheckoutPage() {
  const useNewCheckout = useFlag("new-checkout-flow");
  const layoutVariant = useVariant("checkout-layout-experiment");

  return useNewCheckout ? (
    <NewCheckoutFlow layout={layoutVariant.name} />
  ) : (
    <LegacyCheckoutFlow />
  );
}

Der entscheidende Vorteil dieser SDKs für Feature Flags in React gegenüber dem eigenen Context: Änderungen im Dashboard werden ohne App-Neuladen live an alle verbundenen Clients gepusht, was Kill-Switch-Szenarien bei akuten Produktionsproblemen deutlich beschleunigt. Der Nachteil ist eine zusätzliche externe Abhängigkeit und, bei den gehosteten Varianten, laufende Kosten pro Nutzer oder Anfrage.

5. Progressive Rollouts: prozentuale Aktivierung und Zielgruppen

Ein zentraler Anwendungsfall für Feature Flags in React Apps ist der progressive Rollout: eine neue Funktion wird zunächst für 5 Prozent der Nutzer aktiviert, dann schrittweise auf 25, 50 und schließlich 100 Prozent erhöht, während Fehlerraten und Kennzahlen beobachtet werden. Dieses Muster reduziert den Blast Radius eines fehlerhaften Features drastisch, verglichen mit einem klassischen Alles-oder-nichts-Release.

Managed Services berechnen die Zugehörigkeit zur Prozent-Kohorte üblicherweise über einen konsistenten Hash der Nutzer-ID, sodass derselbe Nutzer bei jedem Aufruf denselben Flag-Wert erhält, statt bei jedem Seitenaufruf zufällig neu zugeordnet zu werden. Das ist wichtig, um inkonsistentes Nutzererlebnis innerhalb einer Session zu vermeiden.


// Simplified consistent-hash rollout logic (illustrates what SDKs do internally)
async function sha256Hex(input: string): Promise<string> {
  const data = new TextEncoder().encode(input);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  return Array.from(new Uint8Array(hashBuffer))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

async function isInRollout(userId: string, flagName: string, percentage: number): Promise<boolean> {
  const hash = await sha256Hex(`${flagName}:${userId}`);
  // Take the first 8 hex chars as a number between 0 and 0xFFFFFFFF
  const bucket = parseInt(hash.slice(0, 8), 16) / 0xffffffff;
  return bucket < percentage / 100;
}

// Same user always lands in the same bucket for this flag
const enabled = await isInRollout("user-4711", "new-checkout-flow", 25);

Für Feature Flags in React Apps mit einem selbst gebauten System lässt sich diese Logik serverseitig implementieren und das Ergebnis über die API der Frontend-Anwendung mitteilen, statt sie im Client zu berechnen. Managed Services übernehmen diese Berechnung bereits vollständig und bieten zusätzlich Zielgruppen-Filter nach Attributen wie Land, Gerätetyp oder Kundensegment im Dashboard an.

6. Feature Flags in Tests: deterministisches Verhalten erzwingen

Ohne besondere Vorkehrung führen Feature Flags in React Apps zu nicht deterministischen Tests, weil ein Test je nach externem Flag-Zustand mal den einen, mal den anderen Codepfad durchläuft. Die Lösung ist, den Feature-Flag-Context in Tests explizit mit festen Werten zu befüllen, statt echte Netzwerkaufrufe zuzulassen.


// CheckoutPage.test.tsx — deterministic feature flag values in tests
import { render, screen } from "@testing-library/react";
import { FeatureFlagContext } from "../feature-flags/FeatureFlagProvider";
import { CheckoutPage } from "./CheckoutPage";

function renderWithFlags(flags: Record<string, boolean>) {
  return render(
    <FeatureFlagContext.Provider value={{ flags, isLoading: false }}>
      <CheckoutPage />
    </FeatureFlagContext.Provider>
  );
}

test("shows the new checkout flow when the flag is enabled", () => {
  renderWithFlags({ "new-checkout-flow": true });
  expect(screen.getByTestId("new-checkout-flow")).toBeInTheDocument();
});

test("falls back to the legacy checkout when the flag is disabled", () => {
  renderWithFlags({ "new-checkout-flow": false });
  expect(screen.getByTestId("legacy-checkout-flow")).toBeInTheDocument();
});

Dieses Muster stellt sicher, dass beide Codepfade eines Feature Flags in React-Setups tatsächlich getestet werden, unabhängig vom Zustand eines externen Services. Sobald ein Feature-Flag entfernt wird, sollten auch die zugehörigen Tests für den deaktivierten Zustand entfernt werden, was direkt zum nächsten Thema führt: dem geordneten Aufräumen alter Flags.

7. Feature-Flag-Debt vermeiden: Lifecycle und Aufräumen

Der größte praktische Nachteil von Feature Flags in React Apps zeigt sich nicht beim Einführen, sondern beim Vergessen. Jedes Flag, das nach vollständigem Rollout im Code verbleibt, addiert eine bedingte Verzweigung, die niemand mehr braucht, aber jeder Entwickler weiterhin lesen und verstehen muss. Dieses Phänomen wird treffend als Feature-Flag-Debt bezeichnet und wächst in großen Codebasen erfahrungsgemäß unbemerkt an.

Ein klarer Lifecycle hilft dagegen: jedes neue Flag bekommt bei der Erstellung ein geplantes Ablaufdatum und einen verantwortlichen Namen im Ticket-System eingetragen. Automatisierte Reports, die alle Flags auflisten, die seit mehr als 90 Tagen auf 100 Prozent stehen, erinnern Teams aktiv daran, den bedingten Code zu entfernen und das Flag selbst aus dem Managed Service zu löschen. Ohne diese Disziplin sammeln sich Feature Flags in React-Projekten typischerweise Dutzende toter Flags an, die die Codebasis unnötig komplex machen.

8. Server-seitige Flags in React Server Components

Mit React Server Components ergibt sich für Feature Flags in React Apps eine interessante Möglichkeit: die Flag-Auswertung kann komplett auf dem Server stattfinden, bevor überhaupt HTML an den Client gesendet wird. Das vermeidet einen sichtbaren Layout-Sprung, der bei client-seitiger Auswertung entsteht, wenn zuerst der Standardzustand gerendert und danach nach dem Laden der Flags neu gerendert wird.

Da Server Components bei jedem Request neu ausgeführt werden, lässt sich die Flag-Abfrage direkt in die Komponente integrieren, ohne einen zusätzlichen Loading-Zustand im Client zu benötigen. Das deaktivierte Feature erscheint so nie im initial gerenderten HTML, was zusätzlich verhindert, dass technisch versierte Nutzer im HTML-Quelltext Hinweise auf noch unveröffentlichte Funktionen finden.

9. Eigenbau gegen Managed Service im Vergleich

Die Entscheidung zwischen einem selbst gebauten Feature-Flag-System und einem Managed Service hängt von Team-Größe, Komplexität der Regeln und Budget ab. Die folgende Tabelle vergleicht die wichtigsten Eigenschaften.

Kriterium Eigener Context Managed Service
Setup-Aufwand Sehr gering Konto und SDK-Integration nötig
Echtzeit-Updates Nur mit eigener WebSocket-Logik Eingebaut über SSE/WebSocket
Zielgruppen-Segmentierung Muss selbst implementiert werden Im Dashboard konfigurierbar
Kosten Keine zusätzlichen Kosten Pro Nutzer oder Anfrage, außer bei Unleash Self-Hosted
Audit-Log Selbst zu bauen Standardmäßig enthalten

Für kleine Teams mit wenigen, einfachen Flags ist der eigene Context oft der pragmatischere Start in Feature Flags in React Apps, ohne eine externe Abhängigkeit einzugehen. Sobald Zielgruppen-Segmentierung, Echtzeit-Kill-Switches oder Compliance-Anforderungen wie ein Audit-Log wichtig werden, überwiegen die Vorteile eines Managed Service deutlich, insbesondere bei Unleash als selbst hostbare Open-Source-Option.

Mironsoft

Feature-Flag-Architektur und kontrollierte Rollouts für React Apps

Risikoarme Releases mit Feature Flags aufbauen?

Wir konzipieren ein passendes Feature-Flag-System für eure React App, ob leichtgewichtiger eigener Context oder Integration eines Managed Service mit progressiven Rollouts und Kill-Switch.

Architektur-Beratung

Eigenbau oder Managed Service anhand eurer Anforderungen entscheiden

SDK-Integration

LaunchDarkly, Unleash oder Flagsmith sauber in eure React App einbinden

Flag-Lifecycle

Prozesse gegen Feature-Flag-Debt etablieren und bestehende Altlasten aufräumen

10. Zusammenfassung

Feature Flags in React Apps trennen die technische Frage des Deployments von der Produktentscheidung, wann eine Funktion für Nutzer sichtbar wird. Ein selbst gebauter Context reicht für einfache Anwendungsfälle völlig aus und vermeidet externe Abhängigkeiten, während Managed Services wie LaunchDarkly, Unleash und Flagsmith bei komplexer Zielgruppen-Segmentierung und Echtzeit-Updates deutliche Vorteile bieten.

Progressive Rollouts über konsistentes Hashing reduzieren den Blast Radius fehlerhafter Features drastisch, während deterministisches Testen mit fest gesetzten Flag-Werten verhindert, dass die Test-Suite von externem Zustand abhängt. Der wichtigste organisatorische Aspekt bleibt der Lifecycle: ohne geplantes Aufräumen sammeln sich in jedem Projekt mit Feature Flags in React schnell tote Flags an, die die Codebasis unnötig aufblähen.

Feature Flags in React Apps — Das Wichtigste auf einen Blick

Deployment versus Release

Feature Flags entkoppeln, wann Code live geht, von der Entscheidung, wann eine Funktion sichtbar wird.

Eigener Context versus Managed Service

Für einfache Fälle reicht ein React Context, komplexe Zielgruppen-Logik lohnt LaunchDarkly, Unleash oder Flagsmith.

Progressive Rollouts

Konsistentes Hashing der Nutzer-ID sorgt für stabile Zuordnung zur Prozent-Kohorte über mehrere Sitzungen hinweg.

Flag-Lifecycle

Geplante Ablaufdaten und automatisierte Reports verhindern Feature-Flag-Debt in wachsenden Codebasen.

11. FAQ: Feature Flags in React

1Deployment versus Release bei Feature Flags?
Deployment heißt Code läuft, Release heißt Funktion sichtbar. Feature Flags trennen beides voneinander.
2Wann reicht eigener Context aus?
Bei wenigen, einfachen Flags ohne komplexe Segmentierung ist ein eigener Context oft die pragmatischste Lösung.
3Wann lohnt sich LaunchDarkly?
Bei Zielgruppen-Segmentierung, Echtzeit-Updates oder Audit-Log-Bedarf überwiegen die Vorteile deutlich.
4Was ist ein progressiver Rollout?
Schrittweise Aktivierung für einen wachsenden Prozentsatz der Nutzer, während Fehlerraten beobachtet werden.
5Warum konsistentes Hashing?
Ohne konsistentes Hashing würde derselbe Nutzer zufällig unterschiedliche Flag-Werte erhalten, das ist inkonsistent.
6Wie testet man Feature Flags zuverlässig?
Den Context in Tests mit festen Werten befüllen, statt echte Netzwerkaufrufe zuzulassen.
7Was ist Feature-Flag-Debt?
Vergessene Flags, die nach vollständigem Rollout im Code bleiben und die Codebasis unnötig komplex machen.
8Wie verhindert man Feature-Flag-Debt?
Geplante Ablaufdaten pro Flag und automatisierte Reports, die an das Aufräumen erinnern.
9Vorteil in Server Components?
Auswertung findet komplett auf dem Server statt, vermeidet einen sichtbaren Layout-Sprung im Client.
10Ist Unleash eine gute Alternative?
Ja, besonders bei strengen Datenschutzanforderungen, da es Open Source und komplett selbst hostbar ist.