Snelle JavaScript ListView in 2026: Windowing, Virtualisatie en Performance-optimalisatie

Leer hoe je met windowing en virtualisatie performante JavaScript ListViews bouwt. TanStack Virtual, React Virtualized en geavanceerde technieken voor 2026.

Snelle JavaScript ListView in 2026: Windowing, Virtualisatie en Performance-optimalisatie

Moderne webapplicaties moeten vaak duizenden of zelfs honderdduizenden records in lijsten weergeven. Of het nu gaat om e-commerce productcatalogi, social media feeds of admin-dashboards, de performance van een ListView kan de gebruikerservaring aanzienlijk beïnvloeden. Dit artikel onderzoekt de nieuwste technieken voor het optimaliseren van JavaScript ListViews in 2026.

Virtualisatie kan het geheugenverbruik bij lijsten met meer dan 10.000 elementen tot 95% verminderen, terwijl een vloeiende scroll-performance gegarandeerd blijft.

Het Probleem met Grote Lijsten

Wanneer een applicatie een lijst met tienduizend elementen rendert, maakt de browser voor elk element DOM-nodes aan. Dit leidt tot aanzienlijke problemen:

  • Hoog geheugenverbruik: Elke DOM-node vereist geheugen
  • Trage initiële rendertijd: Het aanmaken van duizenden elementen kost tijd
  • Trage scroll-performance: De browser moet alle elementen beheren
  • Layout thrashing: Frequente herberekeningen van de layout
typescript
// Naïeve implementatie - NIET aanbevolen voor grote lijsten
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 Oplossing

Windowing, ook bekend als "virtueel scrollen", is een techniek waarbij alleen de zichtbare elementen plus een kleine buffer erboven en eronder worden gerenderd. De overige elementen worden pas aangemaakt wanneer nodig.

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: De Moderne Standaard

TanStack Virtual (voorheen React Virtual) heeft zich in 2026 gevestigd als de toonaangevende oplossing voor virtualisatie in React-applicaties. De bibliotheek biedt framework-agnostische kernfunctionaliteit met specifieke adapters voor React, Vue, Solid en andere 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 Elementhoogtes

Een van de grootste uitdagingen bij virtualisatie zijn lijsten met elementen van variabele hoogte. TanStack Virtual biedt hiervoor de measureElement-functie die de werkelijke grootte van elk element meet.

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;
      // Schatting op basis van contentlengte
      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 met Virtualisatie

De combinatie van virtualisatie met infinite scrolling maakt het mogelijk om onbeperkte hoeveelheden data te laden en weer te geven met optimale 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-optimalisaties

Naast virtualisatie zijn er andere technieken om de prestaties van ListViews te verbeteren.

Memoization van Lijstelementen

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-optimalisaties

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

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

Content-Visibility voor Niet-gevirtualiseerde Lijsten

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 Virtualisatie en Grids

TanStack Virtual ondersteunt ook horizontale virtualisatie en tweedimensionale 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>
  );
}

Klaar om je Node.js / NestJS gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Debugging en Monitoring

Om performance-problemen te identificeren, kunnen verschillende metrics worden gemonitord.

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;
}

Conclusie

Het implementeren van performante ListViews in JavaScript vereist in 2026 een goed begrip van virtualisatietechnieken. TanStack Virtual biedt een volwassen, type-safe oplossing die naadloos integreert in moderne React-applicaties. Door windowing te combineren met memoization, CSS containment en intelligente laadstrategieën, kunnen zelfs lijsten met honderdduizenden elementen vloeiend worden weergegeven. De gepresenteerde technieken zijn essentieel voor elke data-intensieve webapplicatie en horen tot het standaardrepertoire van moderne frontend-ontwikkeling.

Dagelijkse challenge

Zie jij de bug in Node.js / NestJS?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 15 september 2026

Delen

Gerelateerde artikelen