Schnelle JavaScript ListView 2026: Windowing, Virtualisierung und Performance-Optimierung

Erfahren Sie, wie man mit Windowing und Virtualisierung performante JavaScript ListViews erstellt. React Virtualized, TanStack Virtual und weitere Techniken für 2026.

Schnelle JavaScript ListView 2026: Windowing, Virtualisierung und Performance-Optimierung

Moderne Webanwendungen müssen häufig tausende oder sogar hunderttausende von Datensätzen in Listen darstellen. Ob es sich um E-Commerce-Produktkataloge, Social-Media-Feeds oder Admin-Dashboards handelt – die Performance einer ListView kann das Benutzererlebnis entscheidend beeinflussen. Dieser Artikel untersucht die neuesten Techniken zur Optimierung von JavaScript ListViews im Jahr 2026.

Virtualisierung kann den Speicherverbrauch bei Listen mit 10.000+ Elementen um bis zu 95% reduzieren und gleichzeitig eine flüssige Scroll-Performance gewährleisten.

Das Problem mit großen Listen

Wenn eine Anwendung eine Liste mit zehntausend Elementen rendert, erzeugt der Browser für jedes Element DOM-Knoten. Dies führt zu erheblichen Problemen:

  • Hoher Speicherverbrauch: Jeder DOM-Knoten benötigt Speicher
  • Langsame initiale Renderzeit: Das Erstellen tausender Elemente dauert lange
  • Träge Scroll-Performance: Der Browser muss alle Elemente verwalten
  • Layout-Thrashing: Häufige Neuberechnungen des Layouts
typescript
// Naive Implementierung - NICHT empfohlen für große Listen
function NaiveList({ items }: { items: Item[] }) {
  return (
    <div className="list-container">
      {items.map((item) => (
        <div key={item.id} className="list-item">
          <h3>{item.title}</h3>
          <p>{item.description}</p>
        </div>
      ))}
    </div>
  );
}

Windowing als Lösungsansatz

Windowing, auch als "virtuelles Scrollen" bekannt, ist eine Technik, bei der nur die sichtbaren Elemente sowie ein kleiner Puffer darüber und darunter gerendert werden. Die restlichen Elemente werden erst bei Bedarf erstellt.

typescript
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualizedList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
    overscan: 5,
  });

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          width: '100%',
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualItem.size}px`,
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            <ListItem item={items[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

TanStack Virtual: Der moderne Standard

TanStack Virtual (ehemals React Virtual) hat sich 2026 als die führende Lösung für Virtualisierung in React-Anwendungen etabliert. Die Bibliothek bietet Framework-agnostische Kernfunktionen mit spezifischen Adaptern für React, Vue, Solid und weitere Frameworks.

typescript
import { useVirtualizer, VirtualizerOptions } from '@tanstack/react-virtual';

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  itemHeight?: number | ((index: number) => number);
  overscan?: number;
}

function HighPerformanceList<T extends { id: string }>({
  items,
  renderItem,
  itemHeight = 80,
  overscan = 10
}: ListProps<T>) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: typeof itemHeight === 'function'
      ? itemHeight
      : () => itemHeight,
    overscan,
    measureElement: (element) => element.getBoundingClientRect().height,
  });

  const virtualItems = virtualizer.getVirtualItems();

  return (
    <div
      ref={parentRef}
      className="h-full overflow-auto will-change-transform"
    >
      <div
        className="relative w-full"
        style={{ height: `${virtualizer.getTotalSize()}px` }}
      >
        {virtualItems.map((virtualItem) => (
          <div
            key={items[virtualItem.index].id}
            ref={virtualizer.measureElement}
            data-index={virtualItem.index}
            className="absolute top-0 left-0 w-full"
            style={{
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            {renderItem(items[virtualItem.index], virtualItem.index)}
          </div>
        ))}
      </div>
    </div>
  );
}

Dynamische Elementhöhen

Eine der größten Herausforderungen bei der Virtualisierung sind Listen mit Elementen unterschiedlicher Höhe. TanStack Virtual bietet hierfür die measureElement-Funktion, die die tatsächliche Größe jedes Elements misst.

typescript
function DynamicHeightList({ items }: { items: Post[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const measurementsCache = useRef<Map<string, number>>(new Map());

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: (index) => {
      const cached = measurementsCache.current.get(items[index].id);
      if (cached) return cached;
      // Schätzung basierend auf Inhaltslänge
      const contentLength = items[index].content.length;
      return Math.max(100, Math.min(400, contentLength * 0.5));
    },
    measureElement: (element) => {
      const height = element.getBoundingClientRect().height;
      const id = element.getAttribute('data-id');
      if (id) {
        measurementsCache.current.set(id, height);
      }
      return height;
    },
    overscan: 5,
  });

  return (
    <div ref={parentRef} className="h-screen overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => {
          const item = items[virtualItem.index];
          return (
            <div
              key={item.id}
              ref={virtualizer.measureElement}
              data-index={virtualItem.index}
              data-id={item.id}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                transform: `translateY(${virtualItem.start}px)`,
              }}
            >
              <PostCard post={item} />
            </div>
          );
        })}
      </div>
    </div>
  );
}

Infinite Scrolling mit Virtualisierung

Die Kombination von Virtualisierung mit Infinite Scrolling ermöglicht das Laden und Anzeigen unbegrenzter Datenmengen bei optimaler Performance.

typescript
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';

function InfiniteVirtualList() {
  const parentRef = useRef<HTMLDivElement>(null);

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['items'],
    queryFn: ({ pageParam = 0 }) => fetchItems(pageParam),
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    initialPageParam: 0,
  });

  const allItems = useMemo(
    () => data?.pages.flatMap((page) => page.items) ?? [],
    [data]
  );

  const virtualizer = useVirtualizer({
    count: hasNextPage ? allItems.length + 1 : allItems.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
    overscan: 10,
  });

  const virtualItems = virtualizer.getVirtualItems();

  useEffect(() => {
    const lastItem = virtualItems[virtualItems.length - 1];
    if (!lastItem) return;

    if (
      lastItem.index >= allItems.length - 1 &&
      hasNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage();
    }
  }, [virtualItems, allItems.length, hasNextPage, isFetchingNextPage, fetchNextPage]);

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualItems.map((virtualItem) => {
          const isLoader = virtualItem.index >= allItems.length;

          return (
            <div
              key={virtualItem.key}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                height: `${virtualItem.size}px`,
                transform: `translateY(${virtualItem.start}px)`,
              }}
            >
              {isLoader ? (
                <div className="flex items-center justify-center h-full">
                  <Spinner />
                </div>
              ) : (
                <ItemRow item={allItems[virtualItem.index]} />
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

Performance-Optimierungen

Neben der Virtualisierung gibt es weitere Techniken zur Leistungsverbesserung von ListViews.

Memoization von Listenelementen

typescript
const MemoizedListItem = memo(
  function ListItem({ item }: { item: Item }) {
    return (
      <div className="p-4 border-b border-gray-200">
        <h3 className="font-medium">{item.title}</h3>
        <p className="text-gray-600">{item.description}</p>
      </div>
    );
  },
  (prevProps, nextProps) => prevProps.item.id === nextProps.item.id
);

CSS-Optimierungen

css
.list-container {
  contain: strict;
  will-change: transform;
}

.list-item {
  contain: layout style paint;
  content-visibility: auto;
  contain-intrinsic-size: 0 80px;
}

Content-Visibility für nicht-virtualisierte Listen

typescript
function OptimizedStaticList({ items }: { items: Item[] }) {
  return (
    <div className="list-container">
      {items.map((item) => (
        <div
          key={item.id}
          className="list-item"
          style={{
            contentVisibility: 'auto',
            containIntrinsicSize: '0 80px',
          }}
        >
          <ListItemContent item={item} />
        </div>
      ))}
    </div>
  );
}

Horizontale Virtualisierung und Grids

TanStack Virtual unterstützt auch horizontale Virtualisierung und zweidimensionale Grids.

typescript
function VirtualizedGrid({ items, columns = 4 }: { items: Item[]; columns?: number }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const rows = Math.ceil(items.length / columns);

  const rowVirtualizer = useVirtualizer({
    count: rows,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 200,
    overscan: 2,
  });

  const columnVirtualizer = useVirtualizer({
    horizontal: true,
    count: columns,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 250,
    overscan: 1,
  });

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${rowVirtualizer.getTotalSize()}px`,
          width: `${columnVirtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {rowVirtualizer.getVirtualItems().map((virtualRow) => (
          <Fragment key={virtualRow.key}>
            {columnVirtualizer.getVirtualItems().map((virtualColumn) => {
              const itemIndex = virtualRow.index * columns + virtualColumn.index;
              if (itemIndex >= items.length) return null;

              return (
                <div
                  key={virtualColumn.key}
                  style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: `${virtualColumn.size}px`,
                    height: `${virtualRow.size}px`,
                    transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,
                  }}
                >
                  <GridItem item={items[itemIndex]} />
                </div>
              );
            })}
          </Fragment>
        ))}
      </div>
    </div>
  );
}

Bereit für deine Node.js / NestJS-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

Debugging und Monitoring

Um Performance-Probleme zu identifizieren, können verschiedene Metriken überwacht werden.

typescript
function useVirtualListMetrics(virtualizer: Virtualizer<HTMLDivElement, Element>) {
  const [metrics, setMetrics] = useState({
    renderedCount: 0,
    totalCount: 0,
    scrollOffset: 0,
    visibleRange: { start: 0, end: 0 },
  });

  useEffect(() => {
    const updateMetrics = () => {
      const virtualItems = virtualizer.getVirtualItems();
      setMetrics({
        renderedCount: virtualItems.length,
        totalCount: virtualizer.options.count,
        scrollOffset: virtualizer.scrollOffset ?? 0,
        visibleRange: {
          start: virtualItems[0]?.index ?? 0,
          end: virtualItems[virtualItems.length - 1]?.index ?? 0,
        },
      });
    };

    updateMetrics();
    const unsubscribe = virtualizer.scrollElement?.addEventListener(
      'scroll',
      updateMetrics
    );

    return () => {
      virtualizer.scrollElement?.removeEventListener('scroll', updateMetrics as any);
    };
  }, [virtualizer]);

  return metrics;
}

Fazit

Die Implementierung performanter ListViews in JavaScript erfordert 2026 ein gutes Verständnis von Virtualisierungstechniken. TanStack Virtual bietet eine ausgereifte, typsichere Lösung, die sich nahtlos in moderne React-Anwendungen integriert. Durch die Kombination von Windowing mit Memoization, CSS-Containment und intelligenten Ladestrategien lassen sich selbst Listen mit hunderttausenden Elementen flüssig darstellen. Die vorgestellten Techniken sind essentiell für jede datenintensive Webanwendung und sollten zum Standardrepertoire moderner Frontend-Entwicklung gehören.

Tägliche Challenge

Findest du den Bug in Node.js / NestJS?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 15. September 2026

Teilen

Verwandte Artikel