# Szybka Lista JavaScript w 2026: Windowing, Wirtualizacja i Optymalizacja Wydajności > Poznaj techniki windowing i wirtualizacji do renderowania tysięcy elementów w JavaScript. TanStack Virtual, react-virtuoso i najlepsze praktyki dla wydajnych list w 2026 roku. - Published: 2026-09-15 - Updated: 2026-09-15 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Renderowanie tysięcy elementów w liście JavaScript bez wirtualizacji powoduje tworzenie tysięcy węzłów DOM przez przeglądarkę, co prowadzi do wolnego początkowego renderowania, zacinającego się przewijania i zużycia pamięci, które może spowodować awarię urządzeń mobilnych. Szybkie implementacje ListView w JavaScript w 2026 roku rozwiązują ten problem poprzez windowing: renderowanie tylko 10-20 elementów widocznych w viewport, symulując jednocześnie przewijalny obszar zawierający wszystkie elementy. > **Windowing w jednym zdaniu** > > Windowing renderuje tylko widoczne elementy plus mały bufor (overscan), redukując liczbę węzłów DOM z 10 000 do około 20, niezależnie od rozmiaru listy. ## Dlaczego wydajność przeglądarki spada przy dużych listach Każdy węzeł DOM zużywa pamięć i wymaga od przeglądarki śledzenia jego pozycji podczas obliczeń układu. Lista 10 000 elementów z umiarkowanie złożonym znacznikiem (awatar, dwa spany tekstowe, przycisk) tworzy ponad 40 000 węzłów DOM. Silnik renderujący Chrome przelicza style i pozycje dla wszystkich z nich przy każdym zdarzeniu przewijania. Rezultat: początkowe renderowanie trwa 2-4 sekundy, przewijanie spada do 15fps, a zużycie pamięci rośnie do ponad 500MB. Na mobilnym Safari karta często całkowicie się zawiesza. Wirtualizacja naprawia ten problem, utrzymując stałą liczbę węzłów DOM. Niezależnie od tego, czy tablica danych zawiera 100 czy 100 000 elementów, renderowany DOM pozostaje na poziomie około 20-30 węzłów, utrzymując płaskie zużycie pamięci i wydajność przewijania na poziomie 60fps. ```typescript // VirtualizedList.tsx import { useVirtualizer } from '@tanstack/react-virtual' import { useRef } from 'react' interface Item { id: string name: string email: string } export function VirtualizedList({ items }: { items: Item[] }) { const parentRef = useRef(null) // Create virtualizer instance with estimated row height const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 60, // estimated height in pixels overscan: 5, // render 5 extra items above/below viewport }) return (
{virtualizer.getVirtualItems().map((virtualRow) => { const item = items[virtualRow.index] return (

{item.name}

{item.email}

) })}
) } ``` Ta implementacja renderuje tylko widoczne wiersze plus bufor overscan. Zewnętrzny kontener ustawia całkowitą przewijalną wysokość, sprawiając że pasek przewijania zachowuje się tak, jakby wszystkie elementy istniały w DOM. ## TanStack Virtual: Bezgłowa architektura na 2026 rok [TanStack Virtual](https://tanstack.com/virtual/latest) dostarcza framework-agnostyczne prymitywy wirtualizacji. W przeciwieństwie do react-window czy react-virtuoso, nie oferuje gotowych komponentów - tylko hooki obliczające które elementy renderować i gdzie je pozycjonować. Ta bezgłowa architektura oznacza pełną kontrolę nad stylami i strukturą DOM. Aktualna wersja (3.13) wspiera React, Vue, Solid, Svelte i czysty JavaScript. Kluczowe opcje konfiguracji: | Opcja | Cel | Domyślna wartość | |--------|---------|--------| | `count` | Całkowita liczba elementów | wymagane | | `estimateSize` | Funkcja zwracająca szacowaną wysokość/szerokość elementu | wymagane | | `overscan` | Dodatkowe elementy renderowane poza viewport | 1 | | `getScrollElement` | Zwraca przewijalny kontener | wymagane | | `measureElement` | Niestandardowa funkcja pomiarowa dla dynamicznych rozmiarów | auto | Wartość `overscan` bezpośrednio wpływa na płynność przewijania. Ustawienie zbyt niskiej wartości (0-2) powoduje białe błyski podczas szybkiego przewijania. Ustawienie zbyt wysokiej (20+) niweluje korzyści z wirtualizacji. Dla większości aplikacji, wartość 5-10 zapewnia optymalną równowagę. ```typescript // DynamicHeightList.tsx import { useVirtualizer } from '@tanstack/react-virtual' import { useRef, useCallback } from 'react' interface Message { id: string content: string timestamp: number } export function ChatList({ messages }: { messages: Message[] }) { const parentRef = useRef(null) // measureElement enables dynamic height measurement const virtualizer = useVirtualizer({ count: messages.length, getScrollElement: () => parentRef.current, estimateSize: () => 80, // rough estimate, actual measured later overscan: 8, }) return (
{virtualizer.getVirtualItems().map((virtualRow) => { const message = messages[virtualRow.index] return (

{new Date(message.timestamp).toLocaleTimeString()}

{message.content}

) })}
) } ``` Ref `measureElement` umożliwia automatyczny pomiar wysokości po renderowaniu. Obsługuje to zmienne długości zawartości bez konieczności ręcznego obliczania wysokości. ## react-virtuoso: Bogata w funkcje alternatywa [react-virtuoso](https://virtuoso.dev/) przyjmuje przeciwne podejście: bibliotekę komponentów z pełnym wyposażeniem. Tam gdzie TanStack Virtual wymaga budowania kontenera przewijania i logiki pozycjonowania, react-virtuoso obsługuje to wewnętrznie. Kompromisem jest rozmiar pakietu (37KB vs 12KB dla TanStack Virtual), ale znacznie mniej kodu szablonowego dla typowych wzorców jak listy grupowane, przyklejone nagłówki i interfejsy czatu z odwróconym przewijaniem. ```typescript // VirtuosoList.tsx import { Virtuoso } from 'react-virtuoso' interface User { id: string name: string department: string } export function UserDirectory({ users }: { users: User[] }) { return ( (
{user.name.charAt(0)}

{user.name}

{user.department}

)} /> ) } ``` To osiąga ten sam wynik wirtualizacji przy mniejszej ilości kodu. react-virtuoso automatycznie mierzy elementy, obsługuje dynamiczne wysokości i zapewnia zachowanie pozycji przewijania. Podczas [przygotowania do rozmowy kwalifikacyjnej z wydajności React](/technologies/react-next/interview-questions/styling-css-in-js), zrozumienie kiedy wybrać każdą bibliotekę demonstruje świadomość architektoniczną. TanStack Virtual pasuje do aplikacji już używających TanStack Table lub wymagających framework-agnostycznego kodu. react-virtuoso odpowiada zespołom priorytetyzującym szybkość rozwoju nad rozmiarem pakietu. ## Typowe pułapki i rozwiązania ### Skoki pozycji przewijania przy obrazach Obrazy ładują się asynchronicznie, zmieniając wysokość wiersza po początkowym pomiarze. Wirtualizator nie może przewidzieć tej zmiany, powodując przesunięcie zawartości poniżej. Rozwiązanie: zarezerwowanie miejsca dla obrazów używając aspect-ratio lub stałych wymiarów. ```typescript // ImageListItem.tsx interface ImageItem { id: string src: string title: string } export function ImageListItem({ item }: { item: ImageItem }) { return (
{/* Reserve 80x80px space regardless of load state */}
{item.title}

{item.title}

) } ``` ### Utrata stanu podczas przewijania Zwirtualizowane elementy są odmontowywane podczas przewijania poza widoczny obszar. Każdy lokalny stan (pola formularzy, rozwinięte akordeony) znika. Rozwiązanie: przeniesienie stanu do komponentu nadrzędnego lub biblioteki zarządzania stanem, z kluczem według ID elementu. ```typescript // ExpandableList.tsx import { useState, useCallback, useRef } from 'react' import { useVirtualizer } from '@tanstack/react-virtual' interface FAQ { id: string question: string answer: string } export function FAQList({ faqs }: { faqs: FAQ[] }) { // State lives in parent, survives row unmounting const [expandedIds, setExpandedIds] = useState>(new Set()) const parentRef = useRef(null) const toggleExpanded = useCallback((id: string) => { setExpandedIds(prev => { const next = new Set(prev) if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) }, []) const virtualizer = useVirtualizer({ count: faqs.length, getScrollElement: () => parentRef.current, estimateSize: () => 60, overscan: 5, }) return (
{virtualizer.getVirtualItems().map((virtualRow) => { const faq = faqs[virtualRow.index] const isExpanded = expandedIds.has(faq.id) return (
) })}
) } ``` ### Niestabilne klucze powodujące ponowne renderowanie Używanie indeksów tablicy jako kluczy działa, dopóki lista nie zostanie przesortowana. Elementy otrzymują zły stan, powodując wizualne błędy i zmarnowane renderowania. Zawsze należy używać stabilnych, unikalnych identyfikatorów z samych danych: ```typescript // Correct: stable key from data {virtualizer.getVirtualItems().map((virtualRow) => { const item = items[virtualRow.index] return
...
// id from database/API })} // Wrong: index changes when list reorders {virtualizer.getVirtualItems().map((virtualRow, index) => { return
...
// causes bugs on reorder })} ``` ## Porównanie bibliotek dla projektów w 2026 roku Wybór odpowiedniej biblioteki wirtualizacji zależy od wymagań projektu. To porównanie odzwierciedla ekosystem w 2026 roku: | Kryterium | TanStack Virtual | react-virtuoso | react-window | |----------|-----------------|----------------|---------------| | Rozmiar pakietu | 12KB | 37KB | 6KB | | Dynamiczne wysokości | Ręczna konfiguracja | Automatycznie | Brak wsparcia | | TypeScript | Natywne | Natywne | Pakiet @types | | Wsparcie frameworków | React, Vue, Solid, Svelte | Tylko React | Tylko React | | Utrzymanie | Aktywne (2026) | Aktywne (2026) | Tryb utrzymania | | Krzywa uczenia | Wyższa | Niższa | Najniższa | react-window pozostaje opcją dla prostych list o stałej wysokości, gdzie rozmiar pakietu ma znaczenie. Jednak jego status trybu utrzymania i brak wsparcia dla dynamicznych wysokości czyni go nieodpowiednim dla nowych projektów ze złożonymi wymaganiami. Dla [budowania wydajnych aplikacji React](/blog/react-next/react-testing-2026-vitest-rtl-best-practices), zrozumienie kompromisów wirtualizacji demonstruje świadomość wydajności, której szukają rekruterzy. ## Mierzenie usprawnień wydajności Kwantyfikacja korzyści z wirtualizacji wymaga spójnej metodologii pomiarowej. Panel Performance w Chrome DevTools dostarcza odpowiednich metryk. Kluczowe pomiary przed/po wirtualizacji: - **First Contentful Paint (FCP)**: Czas do pierwszego renderowania zawartości. Wirtualizacja redukuje go poprzez ograniczenie początkowego tworzenia DOM. - **Total Blocking Time (TBT)**: Blokowanie głównego wątku podczas ładowania. Mniej węzłów DOM oznacza mniej obliczeń stylu/układu. - **Zużycie pamięci**: Snapshot rozmiaru sterty w panelu Memory. Powinno pozostać stałe niezależnie od długości listy. - **FPS przewijania**: Nagrywanie śladu Performance podczas przewijania. Liczba klatek powinna utrzymywać się na poziomie 60fps. ```typescript // PerformanceMonitor.tsx import { useEffect, useRef } from 'react' export function useScrollFPS(containerRef: React.RefObject) { const frameCountRef = useRef(0) const lastTimeRef = useRef(performance.now()) useEffect(() => { const container = containerRef.current if (!container) return let rafId: number const measureFrame = () => { frameCountRef.current++ const now = performance.now() if (now - lastTimeRef.current >= 1000) { console.log(`Scroll FPS: ${frameCountRef.current}`) frameCountRef.current = 0 lastTimeRef.current = now } rafId = requestAnimationFrame(measureFrame) } const handleScroll = () => { if (!rafId) { rafId = requestAnimationFrame(measureFrame) } } container.addEventListener('scroll', handleScroll, { passive: true }) return () => { container.removeEventListener('scroll', handleScroll) cancelAnimationFrame(rafId) } }, [containerRef]) } ``` Ten hook loguje FPS przewijania podczas rozwoju. Aplikacje produkcyjne powinny używać narzędzi obserwacyjnych jak Sentry Performance lub niestandardowej telemetrii. ## Kluczowe wnioski dla wydajności list JavaScript - Wirtualizacja redukuje węzły DOM z tysięcy do około 20, utrzymując stałą pamięć i wydajność przewijania niezależnie od rozmiaru danych - TanStack Virtual (12KB) dostarcza bezgłowe prymitywy dla maksymalnej kontroli w React, Vue, Solid i Svelte - react-virtuoso (37KB) oferuje gotowe komponenty z automatyczną obsługą dynamicznych wysokości - react-window pozostaje w trybie utrzymania od 2022 roku i nie wspiera dynamicznych wysokości - Ustawienie overscan między 5-10 zapewnia optymalną płynność przewijania bez niwelowania korzyści z wirtualizacji - Rezerwowanie wymiarów obrazów zapobiega skokom pozycji przewijania podczas asynchronicznego ładowania - Przeniesienie stanów rozwijanych/edytowalnych do komponentu nadrzędnego, ponieważ zwirtualizowane wiersze są odmontowywane podczas przewijania - Używanie stabilnych kluczy z ID danych, nigdy indeksów tablicy, aby zapobiec niezgodnościom stanu przy przesortowaniu - Mierzenie FCP, TBT, pamięci i FPS przewijania przed/po implementacji w celu kwantyfikacji usprawnień --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/node-nestjs/fast-javascript-listview-2026-windowing-virtualization-performance