Listen-Virtualisierung mit react-window in React
Listen-Virtualisierung mit react-window
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Unsere ProductListPage zeigt dank Pagination immer nur 6 Produkte gleichzeitig – kein Performance-Problem. Aber viele reale Listen (Chat-Nachrichten, Bestellhistorie, Log-Einträge) sollen ALLE Einträge scrollbar zeigen, nicht seitenweise. Bei Tausenden von Einträgen wird das DOM selbst zum Flaschenhals – Listen-Virtualisierung löst genau das.
Warum 5.000 DOM-Knoten ein Problem sind
Ein <div> pro Listeneintrag klingt harmlos – bei 5.000 Einträgen erzeugt React aber 5.000+ echte DOM-Knoten gleichzeitig. Der Browser muss sie ALLE ins Layout einberechnen, selbst die, die aktuell weit außerhalb des sichtbaren Bereichs liegen. Das kostet Speicher UND Zeit beim ersten Rendern, bei jedem Scroll-Layout-Update, und bei jeder State-Änderung, die die Liste betrifft.
Die Idee: nur rendern, was sichtbar ist
Virtualisierung rendert zu jedem Zeitpunkt NUR die Einträge, die aktuell (plus ein kleiner Puffer) im sichtbaren Ausschnitt liegen – bei 5.000 Einträgen und einer Bildschirmhöhe, die Platz für 10 hat, werden vielleicht 15 DOM-Knoten gerendert, nicht 5.000. Beim Scrollen werden Einträge, die den sichtbaren Bereich verlassen, aus dem DOM entfernt und durch neue ersetzt – der Container behält trotzdem die "richtige" Gesamthöhe, damit die Scrollbar korrekt aussieht.
react-window installieren
npm install react-windowreact-window ist eine schlanke, fokussierte Bibliothek genau für diesen Zweck (Nachfolger von react-virtualized, mit kleinerem Funktionsumfang, aber deutlich kleinerer Bundle-Größe).
Ein Anwendungsfall: eine lange Bestellhistorie
Um Virtualisierung sinnvoll zu demonstrieren, erweitern wir AccountPage um eine simulierte Bestellhistorie mit 2.000 Einträgen (in einer echten App käme das vom Server – wir erzeugen die Daten hier clientseitig, rein zu Demonstrationszwecken der Virtualisierung):
import { FixedSizeList } from 'react-window';
import { useAuthStore } from '../store/authStore';
const ORDER_COUNT = 2000;
const orders = Array.from({ length: ORDER_COUNT }, (_, i) => ({
id: i + 1,
date: new Date(2024, 0, 1 + i).toLocaleDateString('de-DE'),
total: (20 + ((i * 7) % 180)).toFixed(2),
}));
function OrderRow({ index, style }) {
const order = orders[index];
return (
<div style={style} className="order-row">
Bestellung #{order.id} – {order.date} – ${order.total}
</div>
);
}
function AccountPage() {
const user = useAuthStore((state) => state.user);
const logout = useAuthStore((state) => state.logout);
return (
<div>
<h2>Willkommen zurück, {user.username}!</h2>
<button onClick={logout}>Log out</button>
<h3>Bestellhistorie ({ORDER_COUNT} Einträge)</h3>
<FixedSizeList
height={400}
width="100%"
itemCount={ORDER_COUNT}
itemSize={40}
>
{OrderRow}
</FixedSizeList>
</div>
);
}
export default AccountPage;Die vier entscheidenden Props von FixedSizeList
height: die sichtbare Höhe des Scroll-Containers in Pixeln (NICHT die Höhe des gesamten Inhalts) – bestimmt, wie viele Zeilen gleichzeitig sichtbar sind.itemCount: die GESAMTZAHL aller Einträge – react-window nutzt das, um die korrekte virtuelle Scrollbar-Höhe zu berechnen, OHNE alle Einträge tatsächlich zu rendern.itemSize: die feste Höhe JEDER Zeile in Pixeln –FixedSizeListsetzt voraus, dass ALLE Zeilen gleich hoch sind (für unterschiedlich hohe Zeilen gibt esVariableSizeList, hier nicht behandelt).{{OrderRow}}als Kind-Element (KEIN JSX-Aufruf<OrderRow />!): react-window ruft diese Funktions-Komponente selbst auf und übergibtindexundstyleals Props.
Achtung: style={{style}} in OrderRow ist PFLICHT, nicht optional – react-window setzt darüber die absolute Positionierung (position, top, height) jeder Zeile, damit sie an der richtigen Stelle im virtuellen Scroll-Bereich erscheint. Ohne diesen Style würden alle Zeilen übereinander liegen.
Den Unterschied im Profiler messen
Öffnen Sie den React DevTools Profiler aus dem vorletzten Kapitel, zeichnen Sie das erste Laden von /account auf. Sie werden sehen: NUR etwa 10-15 OrderRow-Instanzen erscheinen im Flame Chart, nicht 2.000 – auch mit den Browser-DevTools (Elements-Tab) sehen Sie im echten DOM nur eine Handvoll .order-row-Elemente, egal wie lange die Liste "eigentlich" ist.
Wann sich der Aufwand lohnt
- Listen mit HUNDERTEN bis TAUSENDEN Einträgen, die auf einmal (ohne Pagination) angezeigt werden sollen.
- Chat-Verläufe, Aktivitäts-Feeds, Log-Viewer, große Tabellen – überall dort, wo "unendliches Scrollen" gewünscht ist.
- NICHT nötig für unsere
ProductListPagemit ihren 6 Einträgen pro Seite – Virtualisierung für eine derart kleine Liste wäre reine Komplexität ohne Nutzen.
Tipp: Faustregel: Pagination (Kapitel 24 aus "React für Einsteiger") und Virtualisierung lösen VERWANDTE, aber unterschiedliche Probleme. Pagination reduziert, wie viele Daten überhaupt vom Server geladen werden. Virtualisierung reduziert, wie viele DOM-Knoten für BEREITS geladene Daten existieren. Große Apps kombinieren oft beides: Server liefert z. B. 500 Einträge auf einmal (statt 6-seitiger Pagination), das Frontend virtualisiert deren Darstellung.