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.

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
// 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.
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.
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.
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.
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
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
.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
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.
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.
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.
Zie jij de bug in Node.js / NestJS?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter 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

NestJS en Redis in 2026: Caching, Sessies en Technische Sollicitatievragen
NestJS Redis-integratie met @nestjs/cache-manager, sessiebeheer met connect-redis en veelvoorkomende sollicitatievragen voor Node.js backend-ontwikkelaars.

High Performance JavaScript ListView in 2026: Virtualisatie en Optimalisatietechnieken
Ontdek hoe je met JavaScript list virtualization en React Virtualized hoogperformante ListViews ontwikkelt. Deep-dive in rendering optimalisatie voor grote datasets.

NestJS en MongoDB in 2026: Mongoose, Aggregaties en Sollicitatievragen
Praktische handleiding voor NestJS 12 met MongoDB en Mongoose 9: schema-ontwerp, aggregation pipelines, transacties en technische sollicitatievragen voor backend-ontwikkelaars.