# Što ćete izgraditi (i zašto virtualizacija funkcionira)#
Performanse React listi često se uruše iz jednog jednostavnog razloga: DOM postane prevelik. Renderiranje tisuća čvorova povećava layout, ponovni izračun stilova, memoriju i React reconciliation. Virtualizacija to rješava tako da renderira samo ono što je vidljivo u scroll viewportu plus mali buffer.
U praksi, virtualizacija često smanjuje broj DOM čvorova za 90 do 99 posto. Na primjer, lista od 10.000 redaka renderirana “normalno” može lako stvoriti 10.000 do 50.000 DOM čvorova ovisno o složenosti reda. Virtualizirana lista može držati montiranih samo 30 do 120 redaka u bilo kojem trenutku — što je smanjenje s 10.000 na oko 60 renderiranih redaka za tipične viewporte.
Ovaj vodič pokazuje kako implementirati:
- Virtualizaciju lista i gridova visokih performansi s TanStack Virtual
- Beskonačno učitavanje koje ne trza tijekom brzog skrolanja
- Sticky zaglavlja koja ostaju poravnata dok je sadržaj “windowan”
- Profiliranje kako biste dokazali mjerljiva poboljšanja
- Produkcijsku checklistu za rubne slučajeve: dinamičke visine, resize i pristupačnost
Za dublje obrasce React performansi koji nadopunjuju virtualizaciju, pogledajte profiliranje React performansi, memoizacija, obrasci renderiranja, a za šira razmatranja performansi na razini cijele stranice pročitajte optimizacija performansi web stranice.
# Kada virtualizacija pomaže (a kada odmaže)#
Virtualizacija je najvrjednija kada je usko grlo DOM i trošak renderiranja. Manje je vrijedna kada je usko grlo mreža, skupe slike ili težak CPU rad po itemu koji se i dalje događa za svaki vidljivi red.
Koristite virtualizaciju kada#
| Scenarij | Tipični simptomi | Očekivani učinak |
|---|---|---|
| Lista/log viewer s 1.000+ redaka | Trzanje pri skrolanju, input lag, dugi commitovi u React Profileru | Veliko smanjenje DOM-a, glađi scroll |
| Data tablica s mnogo redaka i stupaca | Spor početni render, velika potrošnja memorije | Veliko poboljšanje, posebno uz virtualizaciju stupaca |
| Grid kartica (thumbnailovi, proizvodi) | CPU skokovi tijekom skrolanja | Manje layout i repaint rada |
| Beskonačni feed s paginacijom | Trzanje pri dodavanju novih stranica | Stabilno skrolanje uz buffered overscan |
Izbjegnite ili odgodite virtualizaciju kada#
| Scenarij | Zašto može biti loš fit | Bolji pristup |
|---|---|---|
| Manje od ~200 jednostavnih stavki | Kompleksnost nadmašuje dobitak | Ostavite jednostavno, optimizirajte renderiranja |
| Redovi trebaju nativnu pretragu u pregledniku i selekciju preko cijele stranice | U DOM-u su samo vidljivi redovi | Napravite poseban UI za pretragu ili server-side search |
| Kompleksno upravljanje fokusom s mnogo interaktivnih kontrola po redu | Unmountani elementi gube stanje fokusa | Razmislite o paginaciji ili redizajnu interakcija u redu |
| SEO zahtijeva sav sadržaj u DOM-u | Virtualizacija skriva sadržaj | SSR-ajte sažetak, paginirajte ili renderirajte manje stavki |
🎯 Ključna poruka: Virtualizacija je alat za UI performanse, ne alat za dohvat podataka. Koristite je za smanjenje DOM-a i render rada, a zatim je uparite s paginacijom ili beskonačnim učitavanjem za skaliranje podataka.
# Preduvjeti i postavljanje#
| Zahtjev | Verzija | Napomene |
|---|---|---|
| React | 18+ | Radi s concurrent renderingom |
| TanStack Virtual | 3+ | @tanstack/react-virtual |
| Browser API-ji | ResizeObserver preporučen | Pomaže kod dinamičkog mjerenja |
Instalacija:
npm i @tanstack/react-virtualImplementirat ćete “scroll element” container, a zatim će virtualizer izračunati koji se itemi trebaju renderirati i gdje.
# Korak 1: Virtualizirajte listu s fiksnom visinom (najbrža baza)#
Krenite s fiksnim visinama kad god je moguće. Fiksna visina daje najstabilnije performanse jer nije potrebno runtime mjerenje.
Minimalni primjer liste s fiksnom veličinom#
import React, { useMemo, useRef } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'
export function VirtualizedList() {
const parentRef = useRef<HTMLDivElement | null>(null)
const items = useMemo(
() => Array.from({ length: 10000 }, (_, i) => `Row ${i}`),
[]
)
const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
overscan: 8,
})
return (
<div
ref={parentRef}
style={{ height: 500, overflow: 'auto', border: '1px solid #ddd' }}
>
<div
style={{
height: rowVirtualizer.getTotalSize(),
position: 'relative',
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: virtualRow.size,
transform: `translateY(${virtualRow.start}px)`,
display: 'flex',
alignItems: 'center',
padding: '0 12px',
boxSizing: 'border-box',
borderBottom: '1px solid #f0f0f0',
}}
>
{items[virtualRow.index]}
</div>
))}
</div>
</div>
)
}Zašto je ovo važno:
getTotalSize()kreira ispravnu visinu skrola bez renderiranja svih redaka.- Apsolutno pozicioniranje sprječava preglednik da radi layout za tisuće “sibling” elemenata.
overscansprječava prazne rupe tijekom brzog skrolanja renderiranjem buffera.
Podešavanje overscana s realnim brojkama#
Overscan je kompromis: veći overscan povećava CPU i memoriju, ali smanjuje šansu da vidite “blanking” tijekom brzog skrolanja. Na modernim uređajima, 8 do 20 je dobar početni raspon za liste.
| Tip sadržaja | Preporučeni overscan | Razlog |
|---|---|---|
| Jednostavni tekstualni redovi | 6 do 12 | Brzo se renderira, mali rizik od praznina |
| Kompleksni redovi (avatari, meniji) | 12 do 24 | Izbjegava vidljivo “pop-in” montiranje |
| Teški mediji (slike, chartovi) | 4 do 10 | Previše overscana može povećati potrošnju memorije |
💡 Savjet: Ako korisnici “fling scrollaju” na trackpadu, povećajte overscan prije nego krenete prepisivati row komponente. Često je to najjeftiniji fix.
# Korak 2: Virtualizirajte grid (kartice, galerije, dashboardi)#
Gridovi trebaju konzistentnu strategiju pozicioniranja. Najlakši pristup je virtualizirati retke, gdje svaki “red” sadrži više stupaca. To virtualizer čini jednodimenzionalnim i obično daje dobre performanse.
Virtualizacija grida batchanjem redova#
import React, { useMemo, useRef } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'
export function VirtualizedGrid() {
const parentRef = useRef<HTMLDivElement | null>(null)
const colCount = 4
const cardHeight = 180
const cards = useMemo(
() => Array.from({ length: 5000 }, (_, i) => ({ id: i, title: `Card ${i}` })),
[]
)
const rowCount = Math.ceil(cards.length / colCount)
const rowVirtualizer = useVirtualizer({
count: rowCount,
getScrollElement: () => parentRef.current,
estimateSize: () => cardHeight,
overscan: 6,
})
return (
<div ref={parentRef} style={{ height: 700, overflow: 'auto' }}>
<div style={{ height: rowVirtualizer.getTotalSize(), position: 'relative' }}>
{rowVirtualizer.getVirtualItems().map((vRow) => {
const startIndex = vRow.index * colCount
const rowItems = cards.slice(startIndex, startIndex + colCount)
return (
<div
key={vRow.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: vRow.size,
transform: `translateY(${vRow.start}px)`,
display: 'grid',
gridTemplateColumns: `repeat(${colCount}, minmax(0, 1fr))`,
gap: 12,
padding: 12,
boxSizing: 'border-box',
}}
>
{rowItems.map((c) => (
<div
key={c.id}
style={{
height: cardHeight - 24,
border: '1px solid #e5e5e5',
borderRadius: 10,
padding: 12,
boxSizing: 'border-box',
background: 'white',
}}
>
<strong>{c.title}</strong>
</div>
))}
</div>
)
})}
</div>
</div>
)
}Ako trebate pravu dvodimenzionalnu virtualizaciju (mnogo redaka i mnogo stupaca), TanStack Virtual to može s dva virtualizera — jednim za retke i jednim za stupce. To je posebno relevantno za spreadsheets i široke tablice te se dobro uparuje s TanStack Table. Za taj puni scenarij pogledajte virtualizacija React tablica i beskonačni scroll s TanStack Table.
# Korak 3: Dodajte beskonačno učitavanje bez “glitchanja” skrola#
Virtualizirana lista i dalje može trzati kada dodajete nove stranice ako blokirate main thread ili okinete velika rerenderiranja. Cilj je dohvatiti ranije, efikasno appendati i zadržati stabilne item keyeve.
Obrazac: sentinel red plus prefetch prag#
- Rezervirajte jedan dodatni “loading” red na kraju.
- Kada korisnik skrola blizu kraja, dohvatite sljedeću stranicu.
- Održite stabilno renderiranje s
overscani prefetch pragom.
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'
type Row = { id: string; label: string }
export function VirtualizedInfiniteList() {
const parentRef = useRef<HTMLDivElement | null>(null)
const [rows, setRows] = useState<Row[]>(() =>
Array.from({ length: 200 }, (_, i) => ({ id: String(i), label: `Row ${i}` }))
)
const [isLoading, setIsLoading] = useState(false)
const hasMore = rows.length < 5000
const count = hasMore ? rows.length + 1 : rows.length
const v = useVirtualizer({
count,
getScrollElement: () => parentRef.current,
estimateSize: () => 44,
overscan: 12,
})
const virtualItems = v.getVirtualItems()
const lastItem = virtualItems[virtualItems.length - 1]
useEffect(() => {
if (!lastItem) return
const isAtEnd = lastItem.index >= rows.length - 1
if (!isAtEnd || isLoading || !hasMore) return
setIsLoading(true)
setTimeout(() => {
setRows((prev) => {
const next = prev.length
const more = Array.from({ length: 200 }, (_, i) => ({
id: String(next + i),
label: `Row ${next + i}`,
}))
return prev.concat(more)
})
setIsLoading(false)
}, 400)
}, [lastItem, rows.length, isLoading, hasMore])
const totalSize = useMemo(() => v.getTotalSize(), [v, rows.length, hasMore])
return (
<div ref={parentRef} style={{ height: 520, overflow: 'auto', border: '1px solid #ddd' }}>
<div style={{ height: totalSize, position: 'relative' }}>
{virtualItems.map((item) => {
const isLoader = hasMore && item.index === rows.length
const label = isLoader ? (isLoading ? 'Loading…' : 'Load more…') : rows[item.index].label
return (
<div
key={isLoader ? 'loader' : rows[item.index].id}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: item.size,
transform: `translateY(${item.start}px)`,
padding: '0 12px',
boxSizing: 'border-box',
display: 'flex',
alignItems: 'center',
borderBottom: '1px solid #f0f0f0',
color: isLoader ? '#666' : '#111',
}}
>
{label}
</div>
)
})}
</div>
</div>
)
}Što mjeriti:
- Vrijeme appendanja stranice treba ostati otprilike konstantno kako broj ukupnih redaka raste.
- Skrolanje ne smije pokazivati prazne rupe kada podaci stignu.
- React commitovi trebaju ostati mali tijekom tipičnih scroll interakcija.
⚠️ Upozorenje: Nemojte koristiti index u arrayu kao React key za stvarne podatke. Kada ubacujete, filtrirate ili osvježavate stranice, index keyevi uzrokuju remountanje i gubitak stanja, što je posebno vidljivo u virtualiziranim UI-jevima.
# Korak 4: Sticky zaglavlja koja ostaju poravnata#
Sticky zaglavlja su česta u tablicama, gridovima i grupiranim listama. S virtualizacijom obično želite:
- Zaglavlje koje nije virtualizirano (uvijek u DOM-u)
- Scroll container u kojem je samo body virtualiziran
- Opcionalno: sticky group zaglavlja unutar liste
Obrazac sticky zaglavlja tablice#
Koristite wrapper s fiksnim zaglavljem i scrollabilnim bodyjem. Zaglavlje se ne miče, body je virtualiziran.
| Sloj | Pozicioniranje | Virtualizirano |
|---|---|---|
| Red zaglavlja | sticky ili odvojeni container | Ne |
| Scroll container | overflow auto | N/A |
| Redovi | apsolutno pozicionirani unutar spacera | Da |
Pristup implementaciji:
- 1Renderirajte zaglavlje izvan virtualiziranog bodyja.
- 2Osigurajte da širine stupaca u zaglavlju odgovaraju stupcima u bodyju.
- 3Ako koristite CSS grid ili fiksne širine, držite ih u jednom zajedničkom config objektu.
Ako trebate kompletno rješenje za tablice sa sortiranjem, pinningom i virtualizacijom, krenite od virtualizacija React tablica i beskonačni scroll s TanStack Table.
ℹ️ Napomena: Sticky elementi unutar transformiranog parenta mogu se ponašati neočekivano. Ako koristite
transformna ancestor elementima sticky zaglavlja, preglednik može drugačije tretirati sticky container. Držite sticky zaglavlja izvan transformiranih containera ili koristite odvojeni wrapper za zaglavlje.
# Korak 5: Dinamičke visine redaka (teži dio)#
Stvarne liste rijetko imaju savršeno fiksne visine. Komentari se šire, badgevi se lome u novi red, pojavljuju se error stanja, a fontovi se učitaju kasno. TanStack Virtual podržava mjerenje, ali to morate tretirati kao sustav kojem trebaju stabilna ograničenja.
Najbolje prakse za dinamičke visine#
| Problem | Simptom | Rješenje |
|---|---|---|
| Visina se mijenja nakon renderiranja | Preklapanja, skrol “skače” | Izmjerite element, ponovno izmjerite kad se sadržaj promijeni |
| Responsive layout | Pogrešne procjene nakon resizea | Pozovite virtualizer.measure() na resize |
| Kasno učitani fontovi ili slike | Layout shift tijekom skrolanja | Postavite eksplicitne dimenzije slika, rezervirajte prostor, ponovno izmjerite nakon loada |
| Miješane visine | Loša preciznost skrola | Koristite razuman estimateSize i mjerenje |
Praktičan pristup:
- 1Držite dobar
estimateSize(blizu mediane visine reda). - 2Mjerite stvarne row elemente.
- 3Izbjegavajte skup layout unutar svakog reda.
Ako se sadržaj reda promijeni nakon async updatea, ponovno izmjerite. Radite to namjerno, ne na svaki render.
import React, { useEffect, useRef, useState } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'
export function VirtualizedDynamicRows() {
const parentRef = useRef<HTMLDivElement | null>(null)
const [rows] = useState(() =>
Array.from({ length: 3000 }, (_, i) => ({
id: String(i),
text: i % 7 === 0 ? 'A longer row that will likely wrap on smaller widths.' : 'Short row.',
}))
)
const v = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 52,
overscan: 10,
})
useEffect(() => {
const onResize = () => v.measure()
window.addEventListener('resize', onResize)
return () => window.removeEventListener('resize', onResize)
}, [v])
return (
<div ref={parentRef} style={{ height: 560, overflow: 'auto', border: '1px solid #ddd' }}>
<div style={{ height: v.getTotalSize(), position: 'relative' }}>
{v.getVirtualItems().map((item) => (
<div
key={rows[item.index].id}
ref={v.measureElement}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${item.start}px)`,
padding: 12,
boxSizing: 'border-box',
borderBottom: '1px solid #f0f0f0',
lineHeight: 1.3,
}}
>
<strong>#{rows[item.index].id}</strong> — {rows[item.index].text}
</div>
))}
</div>
</div>
)
}Ovaj obrazac mijenja dio CPU-a za korektnost. Što imate manje dinamičkih promjena, to će se sve osjećati bolje.
# Profiliranje: dokažite dobitak u performansama (ne samo “djeluje brže”)#
Virtualizacija treba biti mjerljiva. Cilj je smanjiti:
- Broj montiranih DOM čvorova
- Trajanje React commitova tijekom skrolanja
- Long taskove na main threadu
Što snimiti prije i poslije#
| Metrika | Alat | Cilj |
|---|---|---|
| Broj DOM čvorova | Chrome DevTools, Elements panel | Smanjenje za 90 posto ili više za velike liste |
| React commit time | React DevTools Profiler | Manji commitovi tijekom skrolanja, manje rerenderiranja |
| Long taskovi | Chrome Performance panel | Manje taskova preko 50 ms tijekom skrolanja |
| Memorija | Chrome Performance ili Task Manager | Manji heap, manje “detached” čvorova |
Preporučeni workflow:
- 1Snimite baseline s nevirtualiziranom listom koristeći istu row komponentu.
- 2Snimite virtualiziranu verziju s istom veličinom dataseta.
- 3Skrolajte konzistentnom brzinom i usporedite React commitove i long taskove.
Za React-specifične obrasce profiliranja i kako interpretirati commit grafove, koristite profiliranje React performansi, memoizacija, obrasci renderiranja.
Praktični savjeti za profiliranje#
- Profilirajte u throttled CPU modu (npr. 4x usporenje) da biste otkrili probleme koje stvarni korisnici na mid-range uređajima osjećaju.
- Za beskonačne liste mjerite “scroll dok se učitava nova stranica”, ne samo idle scroll.
- Pazite na rerenderiranja koja se mogu izbjeći: row komponente ne bi se trebale rerenderirati kada se promijeni nepovezano stanje.
Ako vidite commitove koje pokreće skrolanje, provjerite da ne spremate scroll position u React state. Pustite preglednik da upravlja skrolom, a virtualizer neka izvede što je vidljivo.
# Checklist za pristupačnost i UX (specifično za virtualizaciju)#
Virtualizacija mijenja DOM dok korisnik skrola. To utječe na navigaciju tipkovnicom, screen readere i očekivanja poput “find in page”.
Tipkovnica i fokus#
- 1Osigurajte da fokus ne završi na itemima koji će se odmah unmountati.
- 2Ako redovi sadrže inpute, sačuvajte stanje izvan row komponente (spremite po id-u).
- 3Osigurajte jasne focus stilove i da tab order ostane predvidljiv.
Screen readeri i semantika#
- Preferirajte semantičke containere gdje god možete, ali nemojte forsirati
tablesemantiku ako ne možete održati ispravne odnose. - Dodajte
aria-labelna scroll region kako bi bio otkriven. - Najavite loading stanja za beskonačne liste s aria-live regionom ako je učitavanje automatsko.
“Find in page” i selekcija#
Browser find vidi samo montirani DOM. Za aplikacije s puno podataka, ponudite eksplicitan UI za pretragu i filtrirajte dataset, umjesto oslanjanja na nativni find.
⚠️ Upozorenje: Virtualizirane tablice koje se pretvaraju da su prave HTML tablice često narušavaju očekivanja assistive tehnologija. Ako trebate pravu semantiku tablice radi usklađenosti s pristupačnošću, razmislite o paginaciji ili hibridnom pristupu gdje ograničite broj redaka, ali zadržite pravu strukturu tablice.
# Checklist rubnih slučajeva (spremnost za produkciju)#
Koristite ovo kao checklistu prije lansiranja za bilo koju implementaciju “React virtualization TanStack Virtual”.
| Rubni slučaj | Rizik | Ublažavanje |
|---|---|---|
| Dinamičke visine redaka | Preklapanja i skokovi skrola | Koristite measureElement, dobar estimateSize, ponovno mjerite na resize |
| Slike se učitavaju kasno | Layout shift | Postavite width i height, rezervirajte prostor, po potrebi ponovno mjerite nakon loada |
| Filtriranje i sortiranje | Skok na pogrešan scroll offset | Resetirajte scroll na vrh, stabilni keyevi, izbjegavajte index keyeve |
| Dodavanje novih stranica | Trzanje skrola tijekom fetcha | Prefetch ranije, zadržite overscan, renderirajte loader red |
| Varijabilna veličina containera | Neispravna mjerenja | Pozovite measure() na resize i promjene layouta |
| Poravnanje sticky zaglavlja | Neusklađeni stupci | Zajednički config širina stupaca, zaglavlje izvan transformiranog containera |
| Server rendering | Hydration mismatch | Renderirajte ograničen početni window, izbjegavajte čitanje scroll positiona na prvom paintu |
| Touch uređaji | Prazne rupe pri brzom “flingu” | Povećajte overscan i prefetch prag |
Ako optimizirate cijelu stranicu, a ne samo listu, uskladite virtualizaciju sa širim poboljšanjima poput optimizacije slika, cacheiranja i smanjenja bundle sizea. Koristite optimizacija performansi web stranice kako ne biste popravili jedno usko grlo, a ignorirali veća.
# Ključne poruke#
- Krenite prvo s virtualizacijom fiksne veličine, jer je to najbrža i najjednostavnija baza za isporučiti.
- Podesite
overscanprema složenosti reda i input uređajima, zatim validirajte s React Profilerom i Chrome Performance. - Implementirajte beskonačno učitavanje pomoću loader reda i prefetch praga tako da skrol nikad ne naleti na praznu rupu.
- Dinamičke visine redaka tretirajte kao rubni slučaj prve klase: mjerite elemente, ponovno mjerite na resize i rezervirajte prostor za sadržaj koji se učitava kasno.
- Pristupačnost planirajte eksplicitno: upravljajte fokusom, ponudite UI za pretragu umjesto oslanjanja na browser find i izbjegavajte “fake” table semantiku ako trebate usklađenost.
# Zaključak#
TanStack Virtual je jedan od najpraktičnijih načina da velike React liste i gridovi djeluju trenutno: manje DOM čvorova, manji React commitovi i glađe skrolanje pod stvarnim opterećenjem. Prava pobjeda dolazi kada kombinirate windowing, beskonačno učitavanje i disciplinirano profiliranje kako biste poboljšanje dokazali, a ne pogađali.
Ako želite da pregledamo vašu trenutnu implementaciju liste ili tablice, profiliramo je i isporučimo produkcijski spremnu virtualizaciju sa sticky zaglavljima i beskonačnim učitavanjem, kontaktirajte Samioda putem naše stranice za React i Next.js usluge i uključite snimku ekrana (screen recording) plus veličinu trenutnog dataseta i složenost redaka.
FAQ
Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.
Više iz kategorije Web razvoj
Sve →Next.js Supabase Realtime u 2026: End-to-End nacrt za chat, presence i suradnju
Izgradite production-ready realtime UI s Next.js App Routerom i Supabase Realtime: dizajn sheme, RLS, optimistička ažuriranja, presence, skaliranje i rješavanje problema s dupliciranim eventima i neusklađenim dozvolama.
Autorizacija u Next.js App Routeru: RBAC vs ABAC uz middleware, RLS i obrasce politika
Praktičan vodič za autorizaciju u Next.js App Routeru uz RBAC i ABAC — s provjerama u middlewareu, zaštitama u server komponentama, RLS-om koji se provodi u bazi, matricom odluke i gotovim obrascima politika za copy-paste.
Strategija testiranja Reacta u 2026.: Vitest + React Testing Library + MSW za sigurne releaseove
Pragmatična strategija testiranja React aplikacija u 2026. uz Vitest, React Testing Library i MSW. Naučite realističnu testnu piramidu, smanjite flaky testove i isporučujte s pouzdanjem uz CI-spremne obrasce.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
UX obrasci u Next.js App Routeru: Error Boundaries, Loading UI i streaming kako treba
Praktičan vodič za otporan UX u Next.js App Routeru: struktura route segmenata, error.tsx, loading.tsx, not-found.tsx i Suspense streaming obrasci za djelomično renderiranje, sigurnije dohvaćanje podataka i manje pomaka layouta.
Kontrolna lista za React code review koju koristimo: performanse, pristupačnost i održivost
Praktična kontrolna lista za React code review usmjerena na performanse, pristupačnost i održivost, s primjerima, savjetima za automatizaciju i copy-paste predloškom.
React Query u velikim aplikacijama: invalidacija cachea, paginacija i obrasci mutacija za stvarne aplikacije
Najbolje prakse invalidacije cachea u React Queryju za aplikacije iz stvarnog svijeta: skalabilan dizajn query keyjeva, strategija invalidacije, optimistična ažuriranja, infinite queryji i pozadinski refetch u Next.js App Routeru.