Uvod: Zašto su slike i dalje najveći problem web performansi
Slike čine preko 60% ukupne težine prosječne web-stranice. I ne, to nije neka apstraktna statistika iz prezentacije na konferenciji — to je stvarnost s kojom se svakodnevno suočava svaki web developer i vlasnik stranice. U 2026. godini, kada Google Core Web Vitals izravno utječu na SEO rangiranje, a korisnici očekuju da se stranica učita u manje od dvije sekunde, optimizacija slika nije luksuz. To je apsolutna nužnost.
Zamislite ovo: korisnik na mobilnom uređaju s 4G vezom pokušava otvoriti vašu stranicu. Umjesto sadržaja — prazan ekran dok se učitavaju tri neoptimizirana JPEG-a od po 3 MB. Do trenutka kada se stranica konačno učita, korisnik je već otišao na konkurentsku stranicu. Zvuči dramatično? Možda, ali podaci pokazuju da 53% mobilnih korisnika napušta stranicu ako se učitava dulje od tri sekunde.
Iskreno, to sam i sam vidio u praksi — klijent s prekrasnom stranicom punom fotografija u visokoj rezoluciji, a PageSpeed ocjena jedva 30. Bolno.
Ajmo onda vidjeti što se sve može napraviti. U ovom vodiču proći ćemo kroz sve aspekte optimizacije slika — od modernih formata poput AVIF-a i WebP-a, preko responzivnih slika i lazy loadinga, do strategija za korištenje image CDN-ova. Cilj je da nakon čitanja imate sve znanje potrebno za ozbiljno poboljšanje performansi vašeg weba.
Moderni formati slika: AVIF, WebP i njihove karakteristike
Prije nego što uopće razmišljamo o lazy loadingu ili CDN-ovima, moramo se pozabaviti najosnovnijim pitanjem: u kojem formatu služimo slike? Format ima ogromni utjecaj na veličinu datoteke, a time i na brzinu učitavanja.
AVIF — Budućnost kompresije slika
AVIF (AV1 Image File Format) je format slike temeljen na AV1 video kodeku, razvijenom od strane Alliance for Open Media. U 2026. godini, AVIF je podržan u svim modernim preglednicima — Chrome, Firefox, Safari i Edge — s ukupnom podrškom od otprilike 93% korisnika. To je sasvim solidna pokrivenost.
Ključne prednosti AVIF-a su, moram priznati, prilično impresivne:
- Superiorna kompresija — AVIF datoteke su u prosjeku 50% manje od ekvivalentnih JPEG-ova i 20-30% manje od WebP-a za fotografski sadržaj.
- Visoka dubina boja — Podržava 10-bitnu i 12-bitnu dubinu boja, što znači preciznije boje i manje bandinga u gradijentima.
- HDR podrška — Izvrsna reprodukcija visokog dinamičkog raspona za fotografije bogatih tonova.
- Transparencija — Podržava alfa kanal, za razliku od JPEG-a.
Ali AVIF ima i nedostatke koje morate razumjeti. Enkodiranje je sporije nego za WebP ili JPEG — generiranje AVIF datoteke može trajati 5-10 puta dulje. Također, dekodiranje troši više procesorskog vremena na klijentskoj strani, što na slabijim mobilnim uređajima može biti problem. Maksimalna rezolucija po pojedinom okviru ograničena je na 8K piksela (iako se to može zaobići korištenjem pločica).
WebP — Pouzdani veteran
WebP je Googleov format koji postoji od 2010. godine, a danas uživa podršku od oko 96% svih preglednika. Nije toliko efikasan u kompresiji kao AVIF, ali WebP nudi odličan omjer između veličine datoteke, kvalitete i brzine dekodiranja.
WebP datoteke su u prosjeku 25-35% manje od JPEG-a za lossy kompresiju i 26% manje od PNG-a za lossless kompresiju. Velika prednost WebP-a je brže dekodiranje — slike se renderiraju brže nego AVIF, što može biti važno za stranice s puno slika na jednoj stranici, poput e-commerce kataloga ili galerija.
WebP također podržava animacije, transparenciju i lossless kompresiju. Njegova široka podrška u preglednicima znači da se u većini slučajeva možete osloniti na njega kao primarni format, s AVIF-om kao naprednom opcijom za korisnike s najnovijim preglednicima.
Kada koristiti koji format — praktični vodič
Zlatno pravilo je koristiti kaskadni sustav formata putem HTML <picture> elementa:
<picture>
<source srcset="slika.avif" type="image/avif">
<source srcset="slika.webp" type="image/webp">
<img src="slika.jpg" alt="Opis slike" width="800" height="600">
</picture>
Ovaj pristup osigurava da preglednik uvijek koristi najefikasniji podržani format. Preglednik će pokušati učitati AVIF, zatim WebP, i tek na kraju klasični JPEG. Jednostavno i elegantno. Za različite tipove sadržaja preporučujem sljedeće:
- Fotografije i složene slike: AVIF → WebP → JPEG
- Logotipi, ikone i UI elementi: SVG kad god je moguće; inače WebP lossless ili PNG
- Slike s transparencijom: AVIF ili WebP (JPEG ne podržava alfa kanal)
- Animacije: Video format (MP4) za duže animacije; WebP za kratke animirane elemente
Kompresija: Pronalaženje savršene ravnoteže
Sam format nije dovoljan — ključna je i razina kompresije. Prevelika kompresija uništava kvalitetu, premala čuva nepotrebne bajte. Cilj je pronaći onaj sweet spot gdje je smanjenje veličine maksimalno, a gubitak kvalitete neprimjetan za ljudsko oko.
Zvuči jednostavno, ali u praksi zahtijeva malo eksperimentiranja.
Preporučene razine kvalitete
Na temelju opsežnog testiranja i industrijskih standarda, evo postavki kvalitete koje su se pokazale najboljima za svaki format:
- AVIF: Kvaliteta 60-70 (u alatu poput Squoosh ili Sharp biblioteke). AVIF-ov napredni kodek postiže izvrsne rezultate čak i na nižim razinama kvalitete.
- WebP: Kvaliteta 75-85. WebP je nešto manje efikasan pa zahtijeva višu razinu kvalitete za usporedive vizualne rezultate.
- JPEG: Kvaliteta 80-85 s progresivnim enkodiranjem. Progresivni JPEG učitava sliku u slojevima — prvo grubu verziju, zatim sve detaljniju — što korisnicima daje vizualni sadržaj ranije.
Automatizacija kompresije s Sharp bibliotekom
Sharp je Node.js biblioteka koja koristi libvips za brzu obradu slika. Idealna je za automatizaciju kompresije u build procesu ili na serveru. Evo kompletnog primjera koji generira sve formate:
import sharp from 'sharp';
async function optimizeImage(inputPath, outputDir, filename) {
const image = sharp(inputPath);
const metadata = await image.metadata();
// Generiraj AVIF verziju
await image
.avif({ quality: 65, effort: 6 })
.toFile(`${outputDir}/${filename}.avif`);
// Generiraj WebP verziju
await image
.webp({ quality: 80, effort: 6 })
.toFile(`${outputDir}/${filename}.webp`);
// Generiraj optimizirani JPEG kao fallback
await image
.jpeg({ quality: 82, progressive: true, mozjpeg: true })
.toFile(`${outputDir}/${filename}.jpg`);
console.log(`Optimizirano: ${filename}`);
console.log(` Original: ${metadata.size} bajtova`);
}
// Batch obrada svih slika u direktoriju
import { readdir } from 'fs/promises';
import path from 'path';
async function batchOptimize(inputDir, outputDir) {
const files = await readdir(inputDir);
const imageFiles = files.filter(f =>
/\.(jpg|jpeg|png|tiff?)$/i.test(f)
);
for (const file of imageFiles) {
const name = path.parse(file).name;
await optimizeImage(
path.join(inputDir, file),
outputDir,
name
);
}
}
batchOptimize('./src/images', './dist/images');
Parametar effort kontrolira koliko vremena enkoder troši na optimizaciju. Vrijednost 6 (od 0 do 9) nudi dobar balans između kvalitete kompresije i brzine enkodiranja. Za build procese koji ne zahtijevaju instant rezultate, slobodno povisite na 8 ili 9 za još manje datoteke.
Uklanjanje nepotrebnih metapodataka
Ovo je nešto što se često zaboravi. Mnoge slike sadrže EXIF metapodatke — informacije o kameri, GPS koordinate, thumbnailove i druge podatke koji korisnicima apsolutno nisu potrebni, ali povećavaju veličinu datoteke. U nekim slučajevima metapodaci mogu činiti i do 15% ukupne veličine. Sharp automatski uklanja metapodatke, ali ako koristite druge alate, obavezno uključite tu opciju.
// Sharp automatski uklanja metapodatke
// Ali možete eksplicitno kontrolirati ponašanje:
await sharp(inputPath)
.withMetadata(false) // Ukloni sve metapodatke
.jpeg({ quality: 82 })
.toFile(outputPath);
// Ili zadržite samo rotaciju:
await sharp(inputPath)
.rotate() // Primijeni rotaciju iz EXIF-a prije uklanjanja
.withMetadata(false)
.jpeg({ quality: 82 })
.toFile(outputPath);
Responsivne slike: Pravi piksel za pravi uređaj
U svijetu gdje korisnici pristupaju web-stranicama s uređaja čiji zasloni variraju od 320 piksela (pametni sat) do 5120 piksela (5K monitor), serviranje jedne veličine slike svima je jednostavno — besmisleno. Mobilni korisnik na 375px širokom zaslonu ne treba sliku od 3840 piksela. A desktop korisnik na Retina zaslonu ne bi trebao gledati mutnu sliku od 400 piksela.
srcset i sizes atributi
HTML pruža moćne atribute srcset i sizes koji omogućuju pregledniku da odabere optimalnu veličinu slike na temelju širine viewporta i gustoće piksela zaslona:
<img
srcset="
hero-400w.jpg 400w,
hero-800w.jpg 800w,
hero-1200w.jpg 1200w,
hero-1600w.jpg 1600w,
hero-2400w.jpg 2400w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
60vw
"
src="hero-1200w.jpg"
alt="Hero slika"
width="1200"
height="675"
>
Atribut sizes govori pregledniku koliko će slika biti široka na zaslonu pri različitim veličinama viewporta. Na temelju toga i gustoće piksela uređaja, preglednik bira odgovarajuću sliku iz srcset liste. Ovo je izuzetno efikasno jer se odluka donosi prije učitavanja slike — preglednik zna točno koju datoteku treba preuzeti.
Da damo konkretan primjer: na mobilnom uređaju širine 375px s 2x gustoćom piksela, preglednik traži sliku od otprilike 750px širine — i odabire varijantu hero-800w.jpg. Desktop korisnik s 1440px viewportom i 1x gustoćom dobiva hero-800w.jpg (60% od 1440 = 864px). Retina desktop korisnik s istim viewportom dobiva hero-1600w.jpg. Preglednik radi posao umjesto vas.
Kombinacija s picture elementom za formate i veličine
Za maksimalnu optimizaciju, kombinirajte <picture> element s srcset da biste istovremeno služili optimalne formate i veličine:
<picture>
<source
type="image/avif"
srcset="
hero-400w.avif 400w,
hero-800w.avif 800w,
hero-1200w.avif 1200w,
hero-1600w.avif 1600w
"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
>
<source
type="image/webp"
srcset="
hero-400w.webp 400w,
hero-800w.webp 800w,
hero-1200w.webp 1200w,
hero-1600w.webp 1600w
"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
>
<img
srcset="
hero-400w.jpg 400w,
hero-800w.jpg 800w,
hero-1200w.jpg 1200w,
hero-1600w.jpg 1600w
"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
src="hero-1200w.jpg"
alt="Hero slika web-stranice"
width="1200"
height="675"
loading="eager"
fetchpriority="high"
decoding="async"
>
</picture>
Da, znam — ovo izgleda kao puno HTML-a za jednu sliku. Ali uštede su ogromne. Mobilni korisnik će učitati AVIF sliku od možda 30 KB umjesto JPEG-a od 500 KB. To je razlika od 94% u prenesenim podacima. Vrijedi tog dodatnog markup-a, vjerujte mi.
Automatsko generiranje responsivnih varijanti
Ručno generiranje svih varijanti je, naravno, nepraktično. Nitko ne želi to raditi ručno za svaku sliku na svakom projektu. Automatizirajte to u vašem build procesu:
import sharp from 'sharp';
const WIDTHS = [400, 800, 1200, 1600, 2400];
const FORMATS = [
{ name: 'avif', options: { quality: 65 } },
{ name: 'webp', options: { quality: 80 } },
{ name: 'jpg', options: { quality: 82, progressive: true } },
];
async function generateResponsiveImages(inputPath, outputDir, baseName) {
const image = sharp(inputPath);
const metadata = await image.metadata();
const tasks = [];
for (const width of WIDTHS) {
// Preskoči veličine veće od originala
if (width > metadata.width) continue;
for (const format of FORMATS) {
const outputName = `${baseName}-${width}w.${format.name}`;
const task = sharp(inputPath)
.resize(width, null, { withoutEnlargement: true })
[format.name](format.options)
.toFile(`${outputDir}/${outputName}`);
tasks.push(task);
}
}
await Promise.all(tasks);
console.log(`Generirano ${tasks.length} varijanti za ${baseName}`);
}
generateResponsiveImages(
'./src/images/hero.jpg',
'./dist/images',
'hero'
);
Lazy loading: Učitavajte slike tek kada su potrebne
Lazy loading je tehnika odgođenog učitavanja resursa koji trenutno nisu vidljivi na zaslonu. Umjesto da preglednik učita sve slike odmah pri otvaranju stranice, slike se učitavaju tek kada korisnik skrola do njih (ili kada su tik ispod vidljivog dijela stranice).
Razmislite o tome na trenutak — na prosječnoj web-stranici s 20 slika, samo 3-5 ih je vidljivo pri prvom učitavanju. Ostale se učitavaju nepotrebno rano i troše propusnost koja bi mogla biti usmjerena na kritičnije resurse.
Nativni lazy loading
Moderni preglednici podržavaju nativni lazy loading putem atributa loading="lazy". Implementacija je doslovno trivijalna — dodajte jedan atribut i preglednik se brine za sve ostalo:
<!-- Slike ispod vidljivog dijela stranice -->
<img
src="galerija-01.jpg"
alt="Slika galerije"
width="800"
height="600"
loading="lazy"
decoding="async"
>
<!-- Hero slika iznad vidljivog dijela — NE koristite lazy loading! -->
<img
src="hero.jpg"
alt="Hero slika"
width="1200"
height="675"
loading="eager"
fetchpriority="high"
>
Kritično pravilo: Nikada, ali nikada ne stavljajte loading="lazy" na slike iznad vidljivog dijela stranice (above the fold), osobito ne na LCP element. To će usporiti učitavanje najvažnijeg sadržaja i pogoršati vaš LCP rezultat. Preglednik ionako daje prioritet vidljivim slikama — dodavanje lazy loadinga na njih samo dodaje nepotrebno kašnjenje.
fetchpriority atribut: Fino podešavanje prioriteta učitavanja
Atribut fetchpriority omogućuje preciznu kontrolu redoslijeda učitavanja resursa. Dok lazy loading određuje kada se slika počinje učitavati, fetchpriority određuje njezin prioritet u odnosu na druge resurse. Tri su moguće vrijednosti:
fetchpriority="high"— Za hero/LCP slike koje moraju biti učitane što prije. Preglednik će im dati prednost nad ostalim resursima.fetchpriority="low"— Za dekorativne slike, reklame, thumbnailove ili slike u karuselima koje nisu trenutno vidljive.fetchpriority="auto"— Zadana vrijednost; preglednik sam odlučuje o prioritetu.
Važno upozorenje: Nikada ne kombinirajte loading="lazy" i fetchpriority="high" na istom elementu! To su kontradiktorne upute — jedna govori pregledniku da odgodi učitavanje, druga da ga požuri. Rezultat? Nepredvidljivo ponašanje i potencijalno pogoršanje performansi. Vidio sam to na više projekata i uvijek izaziva glavobolju pri debuggiranju.
Lazy loading pozadinskih slika pomoću IntersectionObservera
Nativni loading="lazy" radi samo za <img> i <iframe> elemente. Za CSS pozadinske slike morate posegnuti za JavaScriptom — konkretno, IntersectionObserver API-jem:
// Lazy loading za pozadinske slike
function lazyLoadBackgrounds() {
const lazyElements = document.querySelectorAll('[data-bg]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const element = entry.target;
const bgUrl = element.dataset.bg;
element.style.backgroundImage = `url(${bgUrl})`;
element.classList.add('bg-loaded');
observer.unobserve(element);
}
});
}, {
rootMargin: '200px 0px' // Počni učitavati 200px prije viewporta
});
lazyElements.forEach(el => observer.observe(el));
}
// Poziv nakon učitavanja DOM-a
document.addEventListener('DOMContentLoaded', lazyLoadBackgrounds);
U HTML-u, umjesto postavljanja pozadinske slike direktno u CSS, koristite data atribut:
<!-- Umjesto: style="background-image: url(velika-slika.jpg)" -->
<div class="hero-sekcija" data-bg="velika-slika.jpg">
<h1>Naslov</h1>
</div>
<style>
.hero-sekcija {
background-color: #f0f0f0; /* Placeholder boja */
background-size: cover;
min-height: 500px;
transition: background-image 0.3s ease;
}
</style>
Parametar rootMargin: '200px 0px' govori IntersectionObserveru da počne učitavati sliku 200 piksela prije nego što element uđe u viewport. To daje pregledniku dovoljno vremena da preuzme sliku prije nego što korisnik skrola do nje, pa korisnik (nadamo se) nikad ne vidi prazan prostor.
Sprječavanje layout shiftova: Dimenzije i aspect ratio
Jedan od najčešćih uzroka loših CLS (Cumulative Layout Shift) rezultata su slike bez definiranih dimenzija. Kada preglednik naiđe na sliku bez širine i visine, ne zna koliko prostora rezervirati — pa se sadržaj ispod slike pomakne kada se slika konačno učita.
Taj pomak je onaj frustrirajući trenutak kada kliknete na link, a stranica se pomakne i umjesto toga kliknete na reklamu. Svi smo to doživjeli.
Uvijek definirajte dimenzije
<!-- LOŠE: Nema dimenzija — uzrokuje layout shift -->
<img src="slika.jpg" alt="Opis">
<!-- DOBRO: Definirane dimenzije — preglednik rezervira prostor -->
<img src="slika.jpg" alt="Opis" width="800" height="600">
<!-- TAKOĐER DOBRO: CSS aspect-ratio -->
<style>
.responsive-image {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
</style>
<img src="slika.jpg" alt="Opis" class="responsive-image">
Moderni preglednici koriste atribute width i height za izračun omjera stranica prije učitavanja slike. Čak i ako CSS postavlja širinu na 100%, preglednik zna koliko visine treba rezervirati. Ovo je iskreno jedna od najjednostavnijih i najutjecajnijih optimizacija koje možete napraviti — doslovno dva atributa.
CSS content-visibility za lazy rendering cijelih sekcija
CSS svojstvo content-visibility ide korak dalje od lazy loadinga slika — ono omogućuje pregledniku da potpuno preskoči renderiranje cijelih sekcija koje nisu vidljive na zaslonu:
.sekcija-ispod-folda {
content-visibility: auto;
contain-intrinsic-size: auto 500px;
/* Preglednik preskače renderiranje ove sekcije
dok korisnik ne skrola do nje */
}
Svojstvo contain-intrinsic-size daje pregledniku naznaku o veličini sekcije, čime se sprječavaju layout shiftovi. Osobito korisno za dugačke stranice s puno slika — poput e-commerce kataloga ili galerija. Na stranici s 50 proizvoda, renderiranje samo vidljivih 8-10 može ubrzati inicijalno učitavanje za 200-400 milisekundi. Nije loše za dva retka CSS-a.
Preloading kritičnih slika za bolji LCP
Dok lazy loading odgađa učitavanje slika koje trenutno nisu potrebne, preloading čini suprotno — govori pregledniku da odmah počne učitavati kritičnu sliku, još prije nego što parser dođe do nje u HTML-u. Ovo je posebno važno za LCP slike koje se nalaze dublje u HTML strukturi ili se učitavaju putem CSS-a ili JavaScripta.
Link rel="preload" za LCP slike
<head>
<!-- Preload hero slike u modernom formatu -->
<link
rel="preload"
as="image"
type="image/avif"
href="hero-1200w.avif"
imagesrcset="
hero-400w.avif 400w,
hero-800w.avif 800w,
hero-1200w.avif 1200w,
hero-1600w.avif 1600w
"
imagesizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
fetchpriority="high"
>
</head>
Dodavanje preloada za hero sliku može smanjiti LCP za 500-800 milisekundi — a to je često razlika između "potrebno poboljšanje" i "dobro" prema Googleovim pragovima. Efekt je posebno izražen kada je LCP slika pozadinska slika u CSS-u, jer preglednik tada ne može otkriti da mu slika treba dok ne parsira i primijeni CSS.
Jedna važna napomena: Preloadajte samo jednu ili dvije kritične slike. Preopterećivanje preglednika s previše preload zahtjeva može imati suprotan efekt i usporiti učitavanje ostalih kritičnih resursa poput CSS-a i JavaScript datoteka. Manje je više.
Image CDN-ovi: Automatska optimizacija na skali
Sve što smo do sada obradili zahtijeva ručni rad ili konfiguraciju build procesa. I za manje projekte, to je sasvim u redu. Ali za veće projekte s tisućama slika, image CDN-ovi nude automatiziranu alternativu koja pokriva sve — kompresiju, konverziju formata, responsivne varijante i globalnu distribuciju.
Kako funkcionira image CDN
Image CDN prima zahtjev za slikom, automatski je optimizira na temelju konteksta (preglednik, uređaj, veličina zaslona) i isporučuje s najbližeg edge servera. Umjesto da generirate sve varijante unaprijed, transformacije se izvode u stvarnom vremenu putem URL parametara:
<!-- Cloudinary primjer -->
<img src="https://res.cloudinary.com/demo/image/upload/w_800,f_auto,q_auto/hero.jpg">
<!-- w_800: širina 800px
f_auto: automatski odabir formata (AVIF/WebP/JPEG)
q_auto: automatska kvaliteta kompresije -->
<!-- Imgix primjer -->
<img src="https://example.imgix.net/hero.jpg?w=800&auto=format,compress">
<!-- w=800: širina 800px
auto=format: automatski format
auto=compress: automatska kompresija -->
<!-- ImageKit primjer -->
<img src="https://ik.imagekit.io/demo/hero.jpg?tr=w-800,f-auto,q-auto">
Parametar f_auto (ili ekvivalent) je posebno zgodan — CDN automatski detektira podršku preglednika putem Accept headera i servira AVIF korisnicima novih Chromea i Firefoxa, WebP korisnicima starijeg Safarija, ili JPEG kao fallback. Vi ne morate ništa ručno konfigurirati niti se brinuti o <picture> elementima. Sve se događa na serveru.
Usporedba vodećih image CDN-ova u 2026.
Tri najpoznatija image CDN-a imaju različite snage i ciljane korisnike:
- Cloudinary — Najkompletnija platforma s ugrađenim pohranom, DAM-om (Digital Asset Management), AI značajkama poput pametnog izrezivanja i uklanjanja pozadine, te podrškom za video. Idealan za veće e-commerce platforme i medijske kuće. Doduše, kredit-bazirano naplaćivanje može postati prilično skupo za projekte s puno transformacija.
- Imgix — Fokusiran na brzinu i jednostavnost. Koristi vaš postojeći storage (Amazon S3, Google Cloud Storage) i vrši transformacije u stvarnom vremenu. Predvidljivo naplaćivanje, ali minimalna mjesečna cijena od 100 USD može biti preskupa za manje projekte.
- Cloudflare Images — Najjeftinija opcija s jednostavnim naplaćivanjem (5 USD/mjesec za 100.000 slika). Integrirana s Cloudflare CDN mrežom pa je globalna pokrivenost izvrsna. Manje transformacijskih mogućnosti od Cloudinaryja, ali za većinu stranica — više nego dovoljno.
Implementacija responsivnih slika s image CDN-om
Image CDN-ovi značajno pojednostavljuju implementaciju responsivnih slika jer ne morate generirati varijante unaprijed — CDN ih stvara na zahtjev:
<!-- Cloudinary responsivne slike s automatskim formatom -->
<img
srcset="
https://res.cloudinary.com/demo/image/upload/w_400,f_auto,q_auto/hero.jpg 400w,
https://res.cloudinary.com/demo/image/upload/w_800,f_auto,q_auto/hero.jpg 800w,
https://res.cloudinary.com/demo/image/upload/w_1200,f_auto,q_auto/hero.jpg 1200w,
https://res.cloudinary.com/demo/image/upload/w_1600,f_auto,q_auto/hero.jpg 1600w
"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
src="https://res.cloudinary.com/demo/image/upload/w_1200,f_auto,q_auto/hero.jpg"
alt="Hero slika"
width="1200"
height="675"
loading="eager"
fetchpriority="high"
>
Pretvaranje animiranih GIF-ova u video
Animirani GIF-ovi su nevjerojatno neefikasni — datoteka od 5 MB lako se pretvara u MP4 video od 250 KB bez primjetnog gubitka kvalitete. To je smanjenje od 95%. Ako vaša stranica još uvijek koristi animirane GIF-ove, ovo je jedna od najlakših optimizacija s najvećim utjecajem na performanse. Nisko viseće voće, kako bi rekli.
<!-- Umjesto: <img src="animacija.gif"> -->
<video
autoplay
loop
muted
playsinline
width="600"
height="400"
>
<source src="animacija.webm" type="video/webm">
<source src="animacija.mp4" type="video/mp4">
</video>
Atributi autoplay, loop, muted i playsinline osiguravaju da se video ponaša identično kao animirani GIF — automatski se pokreće, ponavlja, nema zvuka i prikazuje se inline. Konverziju možete napraviti pomoću FFmpeg-a:
# Pretvori GIF u WebM (VP9) — najmanja datoteka
ffmpeg -i animacija.gif -c:v libvpx-vp9 -b:v 0 -crf 30 -an animacija.webm
# Pretvori GIF u MP4 (H.264) — univerzalna kompatibilnost
ffmpeg -i animacija.gif -movflags faststart -pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" animacija.mp4
Parametar -movflags faststart je bitan jer premješta metapodatke videa na početak datoteke, što omogućuje pregledniku da počne reproducirati video prije nego što ga u potpunosti preuzme. Bez njega, preglednik mora pričekati cijelu datoteku. Mali detalj, velika razlika u korisničkom iskustvu.
Mjerenje utjecaja: Alati i metrike
Optimizacija bez mjerenja je nagađanje. Ozbiljno. Možete imati osjećaj da je stranica brža, ali bez konkretnih brojki — ne znate ništa pouzdano. Evo ključnih alata i metrika koje trebate pratiti.
Lighthouse audit za slike
Google Lighthouse ima specifične audite vezane uz slike koji će vam točno reći što trebate popraviti:
- Serve images in next-gen formats — Provjerava koriste li se WebP ili AVIF umjesto JPEG/PNG. Pokazuje potencijalnu uštedu u kilobajtima za svaku sliku.
- Properly size images — Provjerava služi li se slike prikladne veličine. Ako servirana slika ima 2000px, a prikazuje se u kontejneru od 400px, Lighthouse će to prijaviti.
- Efficiently encode images — Provjerava razinu kompresije i predlaže optimalnije postavke.
- Defer offscreen images — Provjerava koristi li se lazy loading za slike ispod fold-a.
- Image elements have explicit width and height — Provjerava definirane dimenzije za sprječavanje CLS-a.
Praćenje LCP elementa u produkciji
LCP element je najčešće slika. Praćenje LCP-a u stvarnom okruženju (RUM — Real User Monitoring) ključno je jer laboratorijski testovi ne mogu obuhvatiti sve varijacije uređaja, mreža i lokacija vaših stvarnih korisnika:
// Praćenje LCP-a s identifikacijom elementa
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
// Pošalji podatke na analitički endpoint
const lcpData = {
value: lastEntry.startTime,
element: lastEntry.element?.tagName,
url: lastEntry.url,
size: lastEntry.size,
loadTime: lastEntry.loadTime,
renderTime: lastEntry.renderTime,
};
// Provjeri je li LCP element slika
if (lastEntry.url) {
lcpData.resourceLoadDuration =
lastEntry.loadTime - lastEntry.startTime;
}
navigator.sendBeacon('/api/analytics/lcp', JSON.stringify(lcpData));
}).observe({ type: 'largest-contentful-paint', buffered: true });
Ovaj kod ne samo da mjeri LCP vrijeme, već identificira točno koji element je LCP kandidat i koliko je trajalo njegovo učitavanje. Ako je LCP element slika s dugim vremenom učitavanja, znate da trebate optimizirati tu specifičnu sliku ili dodati preloading. Bez ovih podataka, samo nagađate.
Praktična kontrolna lista za optimizaciju slika
Evo kompletne kontrolne liste koju možete koristiti za svaki projekt. Isprintajte je, stavite na zid, podijelite s timom — kako god vam odgovara:
- Format: Koristite AVIF → WebP → JPEG kaskadu putem
<picture>elementa. - Kompresija: AVIF kvaliteta 60-70, WebP 75-85, JPEG 80-85 s progresivnim enkodiranjem.
- Metapodaci: Uklonite EXIF podatke iz svih produkcijskih slika.
- Responsivnost: Generirajte varijante za 400w, 800w, 1200w, 1600w i koristite
srcset+sizes. - Lazy loading: Dodajte
loading="lazy"na sve slike ispod fold-a. - Fetchpriority: Postavite
fetchpriority="high"na LCP/hero slike. - Preloading: Preloadajte LCP sliku u
<head>sekciji. - Dimenzije: Uvijek definirajte
widthiheightili koristite CSSaspect-ratio. - Animacije: Zamijenite GIF-ove s video formatima (WebM/MP4).
- CDN: Za veće projekte koristite image CDN s automatskom optimizacijom.
- SVG: Koristite SVG za logotipe, ikone i jednostavne ilustracije.
- Mjerenje: Redovito pokretajte Lighthouse audite i pratite LCP u produkciji putem RUM-a.
Zaključak: Mali bajti, veliki rezultati
Optimizacija slika nije glamurozan posao — nema tu uzbudljivih novih frameworka ili revolucionarnih API-ja. Ali upravo zato što je tako fundamentalna, njezin utjecaj na performanse je ogroman.
Smanjenje ukupne težine slika za 50-80% nije nerealan cilj. To je standard koji bi svaka web-stranica trebala postići u 2026. godini.
Evo što možete očekivati od pravilne implementacije:
- LCP poboljšanje od 40-60% kombinacijom modernih formata, preloadinga i fetchpriority-ja.
- CLS smanjenje na gotovo nulu definiranjem dimenzija i korištenjem content-visibility.
- Smanjenje podatkovnog prometa za 50-80% što posebno pogoduje mobilnim korisnicima s ograničenim paketima podataka.
- Bolje SEO rangiranje zahvaljujući poboljšanim Core Web Vitals rezultatima.
Počnite s najutjecajnijim promjenama — prebacite se na moderne formate i dodajte lazy loading. Zatim postupno implementirajte responsivne slike, preloading i image CDN. Svaki korak donosi mjerljivo poboljšanje, a kumulativni efekt stvarno transformira iskustvo vaših korisnika.
U sljedećim člancima detaljnije ćemo obraditi optimizaciju JavaScript bundlea i strategije za smanjenje TTFB-a — jer slike su samo jedan dio slagalice web performansi. Ali, po mom mišljenju, zasigurno najvažniji dio.