React Fiber, Concurrent Mode und Prioritäten erklärt
AI generated
</>
{ }
React · Fiber · Concurrent Mode · Performance
React Fiber, Concurrent Mode und Prioritäten erklärt
wie der neue Reconciler deine UI flüssig hält

React Fiber ist nicht nur ein internes Implementierungsdetail – es ist die Grundlage dafür, warum React 18 mit Concurrent Mode, useTransition und Suspense Anwendungen fundamental anders rendert als alle Versionen davor. Wer verstehen will, warum manche Updates dringlicher sind als andere und wie React entscheidet, was als nächstes auf den Bildschirm kommt, muss Fiber und sein Prioritätensystem kennen.

15 Min. Lesezeit Fiber · Concurrent Mode · useTransition · Suspense · Scheduler React 18+ · TypeScript

1. Was React Fiber wirklich ist

React Fiber ist die vollständige Neuentwicklung des React-Reconcilers, die mit React 16 eingeführt wurde und bis heute die Grundlage aller Rendering-Operationen bildet. Der Begriff „Fiber" bezeichnet dabei zweierlei: einerseits die Architektur des neuen Reconcilers als Ganzes, andererseits die einzelnen Datenstrukturen – die Fiber-Knoten – die den Komponentenbaum im Speicher repräsentieren. Wer verstehen will, wie React Fiber und Concurrent Mode funktionieren, muss zunächst das Problem kennen, das Fiber löst: die Blockierung des Main Threads durch synchrones Rendering.

Vor Fiber hat React den gesamten Komponentenbaum in einem einzigen, nicht unterbrechbaren Durchlauf verarbeitet. Bei tiefen Bäumen oder rechenintensiven Komponenten blockierte das den Browser für hunderte Millisekunden – keine Eingaben, keine Animationen, keine visuellen Updates. Fiber löst das, indem es Renderarbeit in kleine Einheiten aufteilt, die der Scheduler einzeln ausführen und jederzeit unterbrechen kann. Das ist die Grundlage, auf der Concurrent Mode und das gesamte Prioritätensystem aufbauen.

2. Der alte Stack-Reconciler und seine Grenzen

Der ursprüngliche Stack-Reconciler in React verarbeitete den Komponentenbaum rekursiv: eine Komponente rendert, ruft ihre Kinder auf, diese rufen ihre Kinder auf, und so weiter bis zu den Blättern des Baums. Diese Rekursion war synchron und konnte nicht unterbrochen werden. Sobald sie begann, musste sie bis zum Ende durchlaufen – der Browser hatte in dieser Zeit keine Möglichkeit, auf Nutzerenteignisse zu reagieren oder einen Frame zu zeichnen. Bei komplexen Applikationen führte das zu messbaren Ruckeln, besonders bei schneller Eingabe in Textfelder, die Listen neu filtern.

Das fundamentale Problem des Stack-Reconcilers war sein Ausführungsmodell: callstack-basierte Rekursion lässt sich von außen nicht unterbrechen. Fiber ersetzt die Rekursion durch eine explizite verknüpfte Liste von Fiber-Knoten und eine Arbeitsschleife, die den Scheduler kooperativ einbeziehen kann. Die Arbeitsschleife fragt nach jedem Fiber-Knoten, ob noch Zeit im aktuellen Frame ist – und gibt andernfalls Kontrolle zurück. Das macht React Fiber zu einem kooperativen, nicht zu einem präemptiven System.

3. Fiber-Knoten: Arbeit in unterbrechbare Einheiten

Jede React-Komponente im Baum entspricht einem Fiber-Knoten – einer JavaScript-Objektstruktur mit Feldern für Typ, Props, State, Effekte und Zeiger auf Kind, Geschwister und Elternteil. Diese verknüpfte Listenstruktur ermöglicht es der Arbeitsschleife, nach jedem Knoten innezuhalten und später exakt dort weiterzumachen, wo sie aufgehört hat. React hält dabei zwei Bäume vor: den aktuellen Baum, der die sichtbare UI repräsentiert, und den Work-in-Progress-Baum, in dem neue Updates verarbeitet werden. Erst wenn der Work-in-Progress-Baum vollständig ist, werden beide atomar vertauscht – das nennt sich „Commit Phase".

Die Render-Phase (auch „Reconciliation Phase") ist unterbrechbar und kann mehrfach wiederholt werden. Wenn React während der Render-Phase ein dringenderes Update entdeckt – etwa eine Tastatureingabe – kann es die laufende Arbeit verwerfen, das dringende Update synchron abarbeiten und danach die unterbrochene Arbeit neu starten. Diese Eigenschaft hat wichtige Konsequenzen für Seiteneffekte: In der Render-Phase darf nichts außerhalb der Komponente verändert werden, weil dieselbe Arbeit mehrfach ausgeführt werden kann. Seiteneffekte gehören ausschließlich in die Commit-Phase via useEffect.


// Understanding fiber work phases — render phase is repeatable, commit is not
import { useState, useEffect, useRef } from 'react';

function FiberPhaseDemo() {
  const [count, setCount] = useState(0);
  const commitCount = useRef(0);

  // Render phase: may run multiple times — no side effects allowed here
  const expensiveValue = computeExpensiveValue(count); // pure, idempotent

  // Commit phase: runs exactly once after React commits to DOM
  useEffect(() => {
    commitCount.current += 1;
    // Safe to interact with DOM, subscriptions, external systems here
    document.title = `Count: ${count} (commits: ${commitCount.current})`;
  }, [count]);

  return (
    <div>
      <p>Value: {expensiveValue}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

function computeExpensiveValue(n: number): number {
  // Must be pure — React Fiber may call render phase multiple times
  return n * n;
}

4. Lanes: das Prioritätensystem von React

React Fiber verwendet seit React 18 ein Bitmasken-System namens Lanes, um Updates nach Dringlichkeit zu klassifizieren. Jede Lane ist eine Bitmaske, und mehrere Lanes können kombiniert werden, um Gruppen von Updates darzustellen, die zusammen verarbeitet werden. Synchrone Updates – etwa direkte DOM-Interaktionen oder flushSync-Aufrufe – erhalten die höchste Priorität. Transaktionsupdates via useTransition erhalten niedrigere Priorität. Updates durch setTimeout oder aus Netzwerkantworten landen auf noch niedrigeren Lanes.

Das Lanes-System löst ein Problem, das sein Vorgänger (das Expiration-Time-Modell) hatte: Priorisierung von Gruppen zusammengehöriger Updates. Mit Bitmasken kann React effizient prüfen, welche Updates zur selben Gruppe gehören, welche zusammen verarbeitet werden dürfen und welche dringender sind als andere – alles ohne teure Datenstrukturen. Für Entwickler ist das Lanes-System transparent: Die öffentliche API – useTransition, useDeferredValue und startTransition – abstrahiert das Prioritätssystem vollständig.

5. Concurrent Mode aktivieren und verstehen

Concurrent Mode wird in React 18 durch den neuen Root-Einstiegspunkt aktiviert: createRoot statt render. Das ist kein gradueller Opt-in auf Komponentenebene – wenn createRoot verwendet wird, gilt Concurrent Mode für den gesamten Baum. Der Unterschied zum Legacy-Modus: Im Legacy-Modus ist jedes setState-Aufruf ein synchrones, unterbrechungsfreies Render. Im Concurrent Mode kann React mehrere Updates bündeln, unterbrechen und neu ordnen. Das Bündeln (Automatic Batching) gilt seit React 18 für alle Updates, auch in setTimeout und Promise-Callbacks.

Automatic Batching ist der erste spürbare Effekt von Concurrent Mode in der Praxis: Mehrere State-Updates im selben Event-Handler führen zu einem einzigen Re-Render statt zu mehreren. Das war in React 17 nur innerhalb von React-Event-Handlern der Fall, in timeouts und promises aber nicht. Mit React 18 gilt das Batching überall automatisch. Wer ausnahmsweise synchrones Flushen braucht – etwa wenn ein Update sofort sichtbar sein muss, bevor der nächste Code läuft – kann flushSync aus react-dom verwenden.


// React 18: createRoot enables Concurrent Mode for the entire tree
import { createRoot } from 'react-dom/client';
import { StrictMode } from 'react';
import App from './App';

// Concurrent Mode root — all features (useTransition, Suspense, etc.) available
const root = createRoot(document.getElementById('root')!);
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

// Automatic batching: both setters cause ONE re-render in React 18
async function handleSave() {
  const data = await fetchData();
  setLoading(false);   // batched
  setData(data);       // batched — single re-render
}

// Force synchronous flush when needed (e.g. measuring DOM before update)
import { flushSync } from 'react-dom';
flushSync(() => {
  setCount(c => c + 1); // renders immediately, before flushSync returns
});
const newHeight = ref.current.getBoundingClientRect().height;

6. useTransition: dringende vs. nicht-dringende Updates

useTransition ist die wichtigste neue API, die Concurrent Mode für Entwickler greifbar macht. Sie trennt UI-Updates in zwei Kategorien: dringend (urgent) und nicht-dringend (transition). Dringende Updates müssen sofort sichtbar sein – Eingaben, Klicks, Scrolling. Nicht-dringende Updates können warten – Suchergebnisse, gefilterte Listen, Seitenübergänge. Wenn ein dringendes Update während eines laufenden Transition-Updates eintrifft, unterbricht React das Transition-Update, verarbeitet das dringende Update synchron und startet das Transition-Update danach neu.

Das Muster ist einfach: Der State-Setter für die teure Operation wird in startTransition verpackt. Der isPending-Boolean aus useTransition ist während der Verarbeitung true und kann genutzt werden, um einen Lade-Zustand im UI anzuzeigen – ohne einen eigenen loading-State. Das Ergebnis: Die Texteingabe bleibt responsiv, auch wenn dahinter eine teure Filterfunktion über 10.000 Datensätze läuft. Die Liste aktualisiert sich, sobald React Zeit hat – ohne den Main Thread zu blockieren.


import { useState, useTransition, useDeferredValue } from 'react';

// Pattern 1: useTransition for explicit urgent vs. non-urgent split
function SearchPage() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<string[]>([]);
  const [isPending, startTransition] = useTransition();

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    // Urgent: update input immediately
    setQuery(e.target.value);

    // Non-urgent: filtering can wait, React may interrupt and restart
    startTransition(() => {
      setResults(filterLargeDataset(e.target.value));
    });
  }

  return (
    <div>
      <input value={query} onChange={handleChange} placeholder="Suchen…" />
      {isPending && <span>Filtern…</span>}
      <ResultList items={results} />
    </div>
  );
}

// Pattern 2: useDeferredValue — defers derived value without explicit transition
function DeferredList({ query }: { query: string }) {
  const deferredQuery = useDeferredValue(query);
  const isStale = deferredQuery !== query;

  return (
    <div style={ { opacity: isStale ? 0.6 : 1 } }>
      <ExpensiveList filter={deferredQuery} />
    </div>
  );
}

7. Suspense und Concurrent Rendering zusammen

Suspense existiert seit React 16.6 für Code-Splitting, aber seine volle Wirkung entfaltet es erst mit Concurrent Mode. Ohne Concurrent Mode rendert Suspense-Fallback synchron und blockiert den gesamten Baum, bis die suspendierte Komponente aufgelöst ist. Mit Concurrent Mode kann React während des Wartens weiter andere Arbeit leisten – es rendert einen alternativen Baum im Hintergrund und zeigt ihn erst an, wenn er bereit ist. Das verhindert, dass Ladebalken flackern, wenn Daten sehr schnell ankommen.

Die Kombination von useTransition und Suspense ist besonders mächtig für Seitennavigation: Wenn ein Transition-Update eine Komponente zum Suspendieren bringt, hält React die aktuelle Seite sichtbar und zeigt isPending als true, bis der neue Inhalt bereit ist – kein Layout-Flash, kein Skeleton zwischen zwei echten Inhalten. Erst wenn das Rendering des neuen Inhalts abgeschlossen ist, wird gewechselt. Dieses Muster heißt „Concurrent Rendering with Suspense" und ist die Grundlage für React Router v6.4+ und Next.js App Router Streaming.

8. Typische Fehler mit Concurrent Mode

Der häufigste Fehler bei der Migration zu Concurrent Mode: Seiteneffekte in der Render-Phase. Wenn eine Komponente während des Renderns externe Variablen ändert, Netzwerkanfragen auslöst oder console.log-Aufrufe mit Nebeneffekten ausführt, führt das zu mehrfachen unerwarteten Ausführungen, weil die Render-Phase wiederholt werden kann. React StrictMode in der Entwicklungsumgebung verdoppelt Render-Aufrufe absichtlich, um genau solche Probleme sichtbar zu machen. In der Produktion passiert das nur, wenn React wirklich unterbricht und neu startet.

Ein zweiter häufiger Fehler: externe Stores, die React nicht kennt. Wenn eine Komponente einen externen Store liest und dieser sich zwischen dem Render des Elternteils und dem Render des Kindes ändert, sehen Eltern und Kind inkonsistente Daten – ein sogenannter Tearing-Fehler. Die Lösung ist useSyncExternalStore, das React mitteilt, wie es den Store abonnieren und konsistent lesen soll. Libraries wie Zustand, Jotai und Redux Toolkit haben das bereits intern implementiert.


import { useSyncExternalStore } from 'react';

// Correct pattern: useSyncExternalStore prevents tearing with external stores
function useWindowWidth() {
  return useSyncExternalStore(
    // subscribe: React calls this to register a change listener
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    // getSnapshot: must return same value if nothing changed (stable reference)
    () => window.innerWidth,
    // getServerSnapshot: for SSR environments
    () => 1024
  );
}

// Anti-pattern: reading mutable external variable in render (causes tearing)
let externalState = { count: 0 }; // shared mutable state

function BrokenComponent() {
  // WRONG: externalState may change between parent and child render
  return <div>{externalState.count}</div>;
}

// StrictMode doubles render calls in development to expose side effects
// If your component logs twice — it has unintentional side effects in render

9. Legacy vs. Concurrent Mode im Vergleich

Die Unterschiede zwischen Legacy-Modus (ReactDOM.render) und Concurrent Mode (createRoot) betreffen nicht nur interne Implementierungsdetails, sondern das beobachtbare Verhalten in mehreren Dimensionen. Bei der Entscheidung, ob eine Migration lohnt, helfen konkrete Vergleiche der betroffenen Szenarien.

Merkmal Legacy Mode (ReactDOM.render) Concurrent Mode (createRoot) Auswirkung
Rendering Synchron, nicht unterbrechbar Interleaved, unterbrechbar Keine Blockierung des Main Threads
Batching Nur in Event-Handlern Überall automatisch Weniger unnötige Re-Renders
useTransition Nicht verfügbar Vollständig unterstützt Responsivität bei teuren Updates
Suspense Nur Code-Splitting, keine Daten Daten + Lazy + Streaming Nahtlose Ladeübergänge
StrictMode Effekte einmal Effekte doppelt (Dev) Seiteneffekte in Render sichtbar

Die Migration von Legacy zu Concurrent Mode erfordert in der Regel drei Schritte: ReactDOM.render durch createRoot ersetzen, externe Stores auf useSyncExternalStore umstellen und Render-Phase-Seiteneffekte identifizieren und in useEffect verschieben. Libraries, die noch nicht für Concurrent Mode geprüft sind, können durch Tearing-Probleme auffallen – ein Blick in das Changelog der verwendeten State-Management-Library lohnt sich vor der Migration.

Mironsoft

React Performance, Concurrent Mode Migration und Frontend-Architektur

React-Applikation auf Concurrent Mode migrieren?

Wir analysieren eure React-Codebasis auf Concurrent-Mode-Kompatibilität, identifizieren Tearing-Risiken und begleiten die Migration von Legacy-Mode zu createRoot – inklusive Prüfung aller externen State-Management-Libraries.

Kompatibilitätsaudit

Render-Phase-Seiteneffekte, externe Stores und Suspense-Grenzen prüfen

Migration

createRoot, useSyncExternalStore und useTransition schrittweise einführen

Performance-Messung

React DevTools Profiler und Interaction Tracing vor/nach Migration auswerten

10. Zusammenfassung

React Fiber ist die Grundlage, auf der alle modernen React-Features aufbauen. Indem der Reconciler Arbeit in unterbrechbare Fiber-Knoten aufteilt und ein Lanes-basiertes Prioritätensystem verwendet, kann React dringende Updates priorisieren, ohne nicht-dringende zu blockieren. Concurrent Mode via createRoot schaltet dieses System für den gesamten Baum frei. Automatic Batching reduziert Re-Renders überall, nicht nur in Event-Handlern. useTransition und useDeferredValue geben Entwicklern direkte Kontrolle über die Priorisierung von UI-Updates.

Die Migration lohnt sich vor allem bei Applikationen mit teuren Render-Operationen, langen Listen oder häufigen State-Updates durch Nutzeingaben. Wer einen externen Store verwendet, muss prüfen, ob er useSyncExternalStore unterstützt, um Tearing zu vermeiden. React StrictMode ist in Concurrent-Mode-Projekten besonders wertvoll: Er macht Render-Phase-Seiteneffekte durch doppelte Ausführung sichtbar, bevor sie in der Produktion zu schwer debuggbaren Problemen werden.

React Fiber & Concurrent Mode — Das Wichtigste auf einen Blick

Fiber-Architektur

Arbeit in unterbrechbare Fiber-Knoten aufgeteilt. Render-Phase wiederhol­bar, Commit-Phase atomar. Lanes ordnen Updates nach Dringlichkeit.

createRoot aktivieren

Concurrent Mode gilt für den gesamten Baum. Automatic Batching überall. flushSync für synchrones Flushen bei Bedarf.

useTransition & Deferred

Dringende und nicht-dringende Updates trennen. isPending für Lade­zustand ohne eigenen State. useDeferredValue als Alternative.

Tearing vermeiden

Externe Stores mit useSyncExternalStore integrieren. StrictMode doppelte Renders enthüllen Render-Phase-Seiteneffekte frühzeitig.

11. FAQ: React Fiber, Concurrent Mode und Prioritäten

1Was ist der Unterschied zwischen React Fiber und Concurrent Mode?
Fiber ist die interne Reconciler-Architektur seit React 16. Concurrent Mode ist der Rendering-Modus, der auf Fiber aufbaut und seit React 18 via createRoot aktiviert wird. Fiber ermöglicht unterbrechbares Rendering; Concurrent Mode macht das nutzbar.
2Muss ich meinen Code komplett umschreiben?
Meist nicht. createRoot statt ReactDOM.render, externe Stores auf useSyncExternalStore umstellen, Render-Phase-Seiteneffekte in useEffect verschieben. StrictMode zeigt, wo Anpassungen nötig sind.
3Was ist Tearing in React?
Inkonsistente Daten eines externen Stores in verschiedenen UI-Teilen, weil React während des Renders den Store neu lesen kann. useSyncExternalStore verhindert das durch Snapshot-Semantik.
4useTransition vs. useDeferredValue?
useTransition wenn du den State-Setter kontrollierst. useDeferredValue wenn du einen von außen kommenden Wert (Prop, URL-Parameter) verzögern willst ohne Zugriff auf den Setter.
5Warum doppelte useEffect-Ausführung in StrictMode?
StrictMode mountet/unmountet/mountet absichtlich neu, um Cleanup-Funktion auf Korrektheit zu prüfen. In der Produktion passiert das nicht. Doppelte Effekte = fehlende Cleanup-Logik.
6Was sind Lanes in React?
Bitmasken zur Klassifizierung von Updates nach Dringlichkeit. Für Entwickler transparent — gesteuert durch useTransition und startTransition. Höhere Lanes werden vor niedrigeren verarbeitet.
7Kann ich Concurrent Mode schrittweise einführen?
Ja, createRoot in einer einzelnen Sub-App einführen während der Rest im Legacy-Mode bleibt. Erlaubt inkrementelles Testen ohne vollständige Umstellung.
8Was ist automatisches Batching in React 18?
Mehrere setState-Aufrufe in Event-Handlern, setTimeout, Promises und nativen Listenern werden zu einem einzigen Re-Render gebündelt. In React 17 nur in synthetischen React-Event-Handlern.
9Wie analysiere ich lange Render-Zeiten?
React DevTools Profiler: Aufzeichnung starten, Interaktion durchführen, Flamegraph analysieren. Rote Balken = lange Render-Zeiten. Concurrent Mode zeigt zusätzlich unterbrochene Renders.
10Funktioniert Suspense für Daten ohne Framework?
Ja, mit Promise-werfen­dem Cache-Muster. React 19 formalisiert das mit use(). Praktisch empfiehlt sich SWR, React Query oder Next.js App Router, die Suspense korrekt implementieren.