CLS optimizacija u 2026: Kako smanjiti Cumulative Layout Shift ispod 0.1
Praktični vodič za smanjenje CLS-a ispod 0.1 u 2026.: rezerviranje prostora za medije, size-adjust za fontove, content-visibility i mjerenje u produkciji s primjerima u React i Next.js.
CLS (Cumulative Layout Shift) optimizacija u 2026. znači držati kumulativni pomak rasporeda ispod 0.1 tako da rezervirate dimenzije za sve medije, koristite aspect-ratio i content-visibility svojstva, učitavate fontove s size-adjust deskriptorom te dinamički sadržaj umećete iznad fold-a samo kao odgovor na korisničku interakciju. CLS mjeri vizualnu stabilnost stranice tijekom cijelog životnog ciklusa sesije i Googleova je metrika koja izravno utječe na rangiranje. Na jednom e-commerce projektu prošle godine spustio sam CLS s 0.35 na 0.04 koristeći upravo ove tehnike, i sve one rade u svim modernim preglednicima.
Dobar CLS rezultat je 0.1 ili niži; iznad 0.25 stranica pada u kategoriju "loše" prema Core Web Vitals pragovima.
Glavni uzročnici layout shifta su slike i video bez dimenzija, web fontovi koji izazivaju FOUT, dinamički ubačene reklame i embedovi te animacije svojstava koja nisu transform.
CSS svojstvo aspect-ratio i HTML width/height atributi rezerviraju prostor prije nego što se medij učita i tako eliminiraju najčešći izvor pomaka.
Font size-adjust deskriptor i font-display: optional spajaju metrike fallback i web fonta tako da swap ne uzrokuje pomak teksta.
content-visibility: auto u kombinaciji s contain-intrinsic-size izolira off-screen blokove i sprječava propagaciju pomaka.
CLS se mjeri kroz Layout Instability API u Performance Observer-u; metrika u CrUX-u koristi 75. percentil sesijskih prozora od 1s do 5s.
Što je Cumulative Layout Shift i kako se računa
Cumulative Layout Shift je metrika koja zbraja sve neočekivane pomake vidljivih elemenata na stranici tijekom korisnikove sesije. Svaki pojedinačni layout shift score je umnožak impact fractiona (koliki dio viewporta su pogođeni elementi zauzimali prije i nakon pomaka, u jedinstvenoj uniji) i distance fractiona (najveća udaljenost koju je bilo koji element pomaknuo, podijeljena s najvećom dimenzijom viewporta).
Od svibnja 2021. Chrome više ne zbraja sve pomake u jedan broj. Umjesto toga koristi session window pristup: pomake grupira u prozore koji počinju s prvim pomakom i završavaju nakon 1 sekunde tišine, s maksimalnim trajanjem prozora od 5 sekundi. CLS koji se izvještava je najveći takav prozor. To znači da pomak tijekom navigacije ne zbraja se s pomakom 10 sekundi kasnije ako je između bilo mirno.
Važno je razumjeti da se ne broje pomaci koji se događaju unutar 500 ms nakon korisničkog inputa (klik, tipka, dodir). Chrome ih označava kao hadRecentInput: true i isključuje iz CLS-a. Pomake izazvane transform ili opacity tranzicijama također se ne broje jer ne mijenjaju layout. Detaljnu specifikaciju nalazite u web.dev dokumentaciji o CLS-u.
Koji je dobar CLS rezultat u 2026.
Google je pragove za CLS zadržao stabilnima od 2020.: 0.1 ili manje znači "dobro", vrijednost između 0.1 i 0.25 je "treba poboljšati", a sve iznad 0.25 je "loše". Za pozitivan utjecaj na Search rangiranje potreban je dobar rezultat na 75. percentilu sesija, što znači da tri četvrtine vaših stvarnih korisnika mora vidjeti CLS ispod 0.1.
U Lighthouse 12 (verzija dostupna u Chrome 132+ iz veljače 2026.) CLS doprinosi 25 % ukupnog Performance score-a, jednako kao LCP. To je značajan udio i zato pomak rasporeda ne smijete tretirati kao kozmetiku. Direktno mijenja vašu vidljivost u rezultatima pretrage. Razumijevanje cjelokupne slike Core Web Vitalsa daje vam i naš vodič za LCP optimizaciju u 4 faze.
Razlika između mjerenja u laboratoriju (Lighthouse) i u stvarnom korisničkom okruženju (RUM, CrUX) zna biti dramatična. Laboratorijski testovi učitavaju stranicu na statičkoj URL adresi bez korisničke interakcije, dok pravi korisnici skrolaju, kliknu i otvaraju modal prozore, i tako otkrivaju shiftove koje sintetički testovi naprosto ne vide. Zato je RUM mjerenje neizbježno.
Što uzrokuje visok CLS
Iz osobnog iskustva, pet izvora pokriva oko 95 % CLS problema koje sam ikad debugirao:
Slike i video bez eksplicitnih dimenzija. Preglednik ih u prvom render passu prikazuje kao 0×0 i potom proširuje na stvarnu veličinu, što gura sve ispod njih.
Web fontovi s font-display: swap. Kada se učita web font različite metrike, redak prekida na drugom mjestu i sav tekst se preraspoređuje.
Dinamički ubačene reklame ili banner cookieja iznad fold-a. To je najgori scenarij, jer pomiču sve što korisnik gleda.
Lazy-loaded embeds (YouTube, Twitter, Instagram) bez rezerviranog kontejnera.
Animacije nečega što nije transform. Animiranje top, left, height ili margin uvijek izaziva layout pass i može se brojati u CLS.
Rezerviranje prostora za slike i video
Najjeftiniji način da eliminirate najveći izvor CLS-a je dodavanje width i height atributa na svaku <img>, <video> i <iframe> oznaku. Suvremeni preglednici (Chrome 79+, Firefox 71+, Safari 14+) iz tih atributa izračunavaju aspect-ratio i rezerviraju prostor prije nego što resurs stigne sa servera.
<!-- Loše: 0×0 dok se slika ne učita, zatim pomak -->
<img src="/hero.jpg" alt="Hero">
<!-- Dobro: rezerviran prostor 1600×900 omjera -->
<img
src="/hero.jpg"
width="1600"
height="900"
alt="Hero"
fetchpriority="high">
Atribute width i height tretirajte kao deklaraciju omjera, ne kao stvarnu pikselsku veličinu. Slika će se i dalje proširiti CSS-om (max-width: 100%; height: auto;), a preglednik će ipak znati pripadajući omjer prije učitavanja.
Za responzivne slike s srcset-om i picture elementom princip ostaje isti: atributi opisuju omjer, ne fiksnu veličinu. Za slike u koje se mijenja omjer između breakpointa, koristite aspect-ratio u CSS-u:
Svojstvo aspect-ratio podržano je u svim glavnim preglednicima od 2021. Kompletnu referencu nalazite na MDN dokumentaciji za aspect-ratio. Iste tehnike vrijede i za <iframe> embed-ove poput YouTubeovog playera, gdje atribute trebate postaviti na omjer 16:9 (560×315 je najčešći).
Web fontovi bez pomaka teksta
Fontovi su drugi po učestalosti uzročnik CLS-a. Kada koristite font-display: swap, preglednik prvo prikaže tekst u fallback fontu (najčešće Arial ili sans-serif), a zatim ga zamijeni s vašim web fontom. Ako te dvije fontne obitelji imaju različite metrike (x-height, advance width), redovi se prelome na drugim mjestima i tekst se naprosto "skoči".
U 2026. rješenje su size-adjust, ascent-override i descent-override deskriptori u @font-face pravilu. Oni omogućuju da fallback font izgleda metrički identično vašem web fontu, pa swap ne uzrokuje pomak:
Točne vrijednosti za size-adjust izračunate su tako da fallback font ima istu visinu malog slova kao stvarni font. Alat Fontaine (npm paket) generira ih automatski iz datoteke vašeg fonta, a Next.js 15 i Astro to rade ugrađeno kroz next/font i astro:assets.
Alternativa za nesmetane stranice je font-display: optional, koji daje fontu 100 ms da se učita; ako ne stigne, koristi se fallback i web font se ne prikazuje u toj sesiji. Time se eliminira swap u potpunosti, po cijenu toga da neki korisnici nikad ne vide vaš brand font na prvom učitavanju.
Preload za kritične fontove
Bez obzira na strategiju, kritične fontove (one koje koristi sadržaj iznad fold-a) preloadirajte:
Najopasniji shift je onaj koji se događa kad korisnik već gleda stranicu: banner za prihvaćanje kolačića koji se ubaci na vrh, reklama koja stigne sekundu kasnije, ili "Novosti!" promo bar. Pravilo je jednostavno. Nikad ne umećite sadržaj iznad postojećeg sadržaja osim kao odgovor na korisničku interakciju.
Za reklame s neizvjesnim dimenzijama rezervirajte najmanju moguću veličinu u kontejneru i pustite reklamu da bude manja ili da se ne prikazuje. Ako AdSense slot može biti 300×250 ili 300×600, rezervirajte 300×250 i prihvatite da 600-piksel varijanta neće biti dostupna. Manje shifta vrijedi više od povremenog dodatnog impressiona (vjerujte mi na riječ).
<div class="ad-slot" style="min-height: 250px; contain: layout;">
<!-- AdSense kod ide ovamo -->
</div>
Cookie banneri trebaju biti overlay (fixed pozicija s z-index), a ne element u toku dokumenta. To je i bolji UX i nula CLS. Ako iz pravnih razloga banner mora biti u toku, postavite ga na dno stranice, ne na vrh.
Za skeleton loadere, vrlo popularne u SPA pristupima, pazite da skeleton ima iste dimenzije kao i konačni sadržaj. Ako vaš skeleton kartice ima visinu 200 px, a stvarna kartica 240 px, dobit ćete shift od 40 px po kartici, što na listi od 10 kartica brzo prelazi prag 0.25.
content-visibility i contain-intrinsic-size
Za duge stranice (proizvodne kataloge, blogove s puno komentara) CSS svojstvo content-visibility: auto u kombinaciji s contain-intrinsic-size drastično ubrzava rendering jer preglednik preskače layout i paint za off-screen sekcije. Ali ima jednu zamku za CLS: bez contain-intrinsic-size, preglednik tretira sekciju kao 0 px visoku i kada postane vidljiva, događa se shift.
.article-section {
content-visibility: auto;
contain-intrinsic-size: auto 600px;
}
Vrijednost auto 600px govori pregledniku: prije nego vidiš stvarni sadržaj, pretpostavi da sekcija zauzima 600 px vertikalno. Kada korisnik dođe blizu, sekcija se renderira i ako je stvarna visina drugačija, shift se dogodi, ali izvan session window-a, pa se ne broji u CLS jer se ne događa iznad ili oko korisnikovog fokusa.
Od Chrome 128 (kolovoz 2024.) contain-intrinsic-size podržava ključnu riječ auto koja "pamti" zadnju izmjerenu veličinu, što je savršeno za infinite scroll liste. Više detalja je u web.dev vodiču za content-visibility.
Kako mjeriti CLS u produkciji
Lighthouse i WebPageTest daju vam laboratorijsku sliku, ali pravi CLS dobivate samo iz stvarnih sesija. Layout Instability API kroz PerformanceObserver omogućuje da hvatate svaki shift i šaljete ga u svoj analytics:
Polje sources u svakom LayoutShift entryju sadrži node referencu na element koji se pomaknuo. To je relativno bezvrijedna informacija u laboratoriju, ali zlato vrijedna u produkciji jer vam točno govori što se pomaknulo. Praćenje CWV-a detaljno pokrivamo u vodiču za praćenje web performansi.
Za jednostavniju integraciju koristite Google web-vitals npm paket. Verzija 4.x (siječanj 2026.) ima ugrađen attribution mod koji vraća selektor pomaknutog elementa, vrijeme i bliske kandidate:
Moderni framework projekti imaju nekoliko ugrađenih CLS rizika koje treba znati.
React i hydration
Server-side rendered HTML s React hidracijom može uzrokovati shift ako klijentska komponenta na prvom renderu prikaže drugačiji sadržaj od serverskog. Najčešći krivac je useEffect koji postavlja state ovisan o window ili localStorage (na ovaj sam se bag nabacao više puta nego što bih priznao):
// Loše: shift od prikazanog state-a
function Banner() {
const [dismissed, setDismissed] = useState(false);
useEffect(() => {
setDismissed(localStorage.getItem('dismissed') === 'true');
}, []);
if (dismissed) return null;
return <div>Banner</div>;
}
Server renderira banner, klijent ga ukloni, i sav sadržaj ispod skoči gore. Rješenje: koristite useSyncExternalStore ili renderirajte banner sakriven (visibility: hidden) dok ne potvrdite state. Detalje o sigurnoj hidraciji nudi službena React dokumentacija.
Next.js Image komponenta
Next.js <Image> komponenta automatski postavlja width i height, ali samo ako joj ih proslijedite, ili ako koristite fill prop unutar relativno pozicioniranog kontejnera s eksplicitnim dimenzijama. fill bez dimenzija na roditelju i dalje uzrokuje shift.
View Transitions
View Transitions API (stabilan u Chrome 126+, podrška u Safari 18 iz 2024.) je tehnika animiranja navigacija. Sam po sebi ne uzrokuje CLS jer animacije idu kroz transform, ali pazite na ::view-transition-old i ::view-transition-new stilove. Ako jedan element ima drugačiji omjer prije i poslije, animacija će glatko teći, a shift će se računati. Konzervativno mjerite CLS prije i poslije uvođenja View Transitions.
Animacije i tranzicije
Animirajte samo transform i opacity. Ako morate animirati visinu kontejnera (otvaranje akordeona), koristite FLIP tehniku ili interpolate-size: allow-keywords svojstvo (Chrome 129+) koje omogućuje height: auto tranzicije bez layout passa.
Često postavljana pitanja
Koji je dobar CLS rezultat za SEO?
Cilj je CLS od 0.1 ili manje na 75. percentilu stvarnih korisničkih sesija. Iznad 0.25 stranica pada u "loše" područje i to negativno utječe na Google Search rangiranje kao dio Core Web Vitals signala.
Zašto je moj CLS visok iako sam dodao width i height na slike?
Najčešća tri preostala uzročnika su: web fontovi sa font-display: swap koji uzrokuju pomak teksta, dinamički ubačene reklame ili cookie banneri iznad fold-a, i lazy-loaded embed sadržaj (YouTube, tweetovi) bez rezerviranog kontejnera. Provjerite sources polje u Layout Instability API-ju da vidite točan element koji se pomiče.
Kako mogu debugirati CLS u Chrome DevTools?
Otvorite Performance panel, snimite učitavanje stranice, i pogledajte traku "Experience". Svaki Layout Shift događaj je tu označen plavim pravokutnikom s prikazom pomaknute regije. U Performance Insights panelu Chrome 132+ dobivate i konkretne preporuke s linkom na element koji izaziva shift.
Brojaju li se animacije u CLS?
Animacije transform i opacity svojstava se ne broje u CLS jer ne mijenjaju layout. Animacije svojstava poput top, left, height, width ili margin izazivaju layout pass i mogu doprinijeti CLS-u. Uvijek koristite transform: translate() umjesto top/left za animacije pozicije.
Je li CLS isto što i layout thrashing?
Ne. CLS mjeri vizualnu stabilnost, odnosno koliko se elementi pomiču na ekranu. Layout thrashing je sinkrono čitanje i zapisivanje DOM-a u JavaScriptu koje izaziva višestruke layout proračune po frameu i uzrokuje pad FPS-a. Layout thrashing utječe na INP, ne na CLS, iako oba mogu signalizirati lošu izvedbu rendera.
Konkretan vodič za smanjenje TTFB-a ispod 200 ms u 2026: pet faza koje čine prvi bajt, kako ih mjeriti kroz PerformanceObserver i Server-Timing, te playbook s CDN edge cacheom, HTTP/3 i streaming SSR-om.
Praktični vodič za optimizaciju INP-a (Interaction to Next Paint) — Core Web Vital metrike odzivnosti. Tehnike poput scheduler.yield(), LoAF API dijagnostike, Web Workers, upravljanja skriptama trećih strana i framework optimizacija za React i Vue.
Naučite kako dijagnosticirati i optimizirati LCP kroz 4 ključne podfaze — TTFB, Resource Load Delay, Load Duration i Render Delay. Praktični vodič s radnim kodom za 2026.