Wróć na blog
obrazy wydajność webp optymalizacja

Obrazki, które zabijają Twoją stronę

Obrazy to zwykle największy ciężar strony. Pokazuję formaty, wymiary, kompresję, lazy-loading i responsywne srcset — z przykładami kodu i wagami w KB.

Franciszek Sikora

Jest jeden powód, dla którego strony ładują się wieczność, i prawie zawsze ten sam: obrazy. Nie skrypty, nie hosting, nie tajemnicza „magia” — po prostu zdjęcia wrzucone prosto z aparatu albo Canvy, ważące po kilka megabajtów każde. Dobra wiadomość jest taka, że to akurat najłatwiejszy do naprawienia element całej układanki. W tym wpisie pokażę dokładnie, gdzie tracisz wagę i jak ją odzyskać — z konkretnym kodem.

Dlaczego to właśnie obrazy

Otwórz dowolną stronę i sprawdź, co waży najwięcej. W zdecydowanej większości przypadków grafika to 50–70% całkowitej wagi. Jeden baner w nagłówku potrafi ważyć więcej niż cały kod HTML, CSS i JavaScript razem wzięty.

Problem polega na tym, że obraz nieoptymalny działa „cicho”. Strona wygląda dobrze, więc nikt nie podejrzewa, że pod spodem przeglądarka ściąga 8 MB zdjęć. A użytkownik na telefonie w terenie po prostu czeka — i często wychodzi. Jeśli chcesz zobaczyć, jak ta zwłoka przekłada się na utracone konwersje, opisałem to w dlaczego strona ładuje się 8 sekund.

Format ma znaczenie — i to ogromne

Ten sam obraz zapisany w różnych formatach potrafi ważyć dramatycznie różnie. Stare formaty (JPEG, PNG, GIF) były projektowane dekady temu. Nowe (WebP, AVIF) kompresują znacznie wydajniej przy tej samej jakości wizualnej.

Porównanie wagi tego samego zdjęcia w PNG, JPEG, WebP i AVIF

Jak widać, przejście z PNG na AVIF potrafi obciąć wagę o 80–90% — przy obrazie, którego oko praktycznie nie odróżni. Oto ściąga, kiedy czego używać:

FormatKiedy używaćUwagi
AVIFDomyślny wybór na dziśNajlepsza kompresja, wspierany przez wszystkie nowoczesne przeglądarki
WebPBezpieczny fallbackTrochę większy od AVIF, ale szerokie wsparcie od lat
JPEGFallback dla starych przeglądarekZdjęcia, gdy musisz mieć maksymalną zgodność
PNGGrafiki z przezroczystością, logo, ikonyCiężki dla zdjęć — nie używaj go do fotografii
GIFPraktycznie nigdyAnimacje rób jako wideo (MP4/WebM) — waży wielokrotnie mniej
SVGLogo, ikony, proste ilustracjeWektor — skaluje się bez utraty jakości, mikroskopijna waga

Najszybsza wygrana w całym wpisie

Jeśli masz na stronie animowane GIF-y, zamień je na wideo MP4/WebM. Animacja, która jako GIF waży 5 MB, jako wideo zmieści się często w 300 KB — przy tej samej, a nawet lepszej jakości.

Właściwe wymiary — nie serwuj 4000 px do kontenera 600 px

To najczęstszy i najboleśniejszy błąd. Wrzucasz zdjęcie prosto z aparatu — 4032 px szerokości, 6 MB — a na stronie wyświetla się ono w kontenerze o szerokości 600 px. Przeglądarka i tak pobiera cały plik, po czym zmniejsza go w locie. Marnujesz 90% pobranych danych.

Zasada jest prosta: obraz nie powinien być znacząco większy niż miejsce, w którym się wyświetla (z zapasem 2x na ekrany Retina). Jeśli kontener ma 600 px, serwuj 1200 px, a nie 4000 px.

Kompresja i narzędzia

Nawet po przeskalowaniu obraz zwykle da się jeszcze odchudzić bez widocznej różnicy. Trzy narzędzia, które polecam:

  • Squoosh (squoosh.app) — darmowy, w przeglądarce, z suwakiem porównującym jakość na żywo. Idealny do pojedynczych obrazów.
  • TinyPNG (tinypng.com) — wrzucasz, dostajesz odchudzony plik. Świetny do szybkiej, masowej kompresji PNG/JPEG.
  • sharp — biblioteka do automatyzacji (Node.js). Gdy chcesz przerobić setki obrazów skryptem.

Przykład wsadowej konwersji do AVIF i WebP przez sharp:

import sharp from "sharp";

const sizes = [400, 800, 1200];

for (const width of sizes) {
  await sharp("zdjecie.jpg")
    .resize(width)
    .avif({ quality: 55 })
    .toFile(`zdjecie-${width}.avif`);

  await sharp("zdjecie.jpg")
    .resize(width)
    .webp({ quality: 70 })
    .toFile(`zdjecie-${width}.webp`);
}

Lazy-loading — ładuj dopiero to, co widać

Po co pobierać obrazek ze stopki strony, skoro użytkownik być może nigdy do niego nie doscrolluje? Lazy-loading odkłada pobieranie obrazów spoza widoku, aż użytkownik się do nich zbliży. W HTML to jeden atrybut:

<img src="zdjecie.jpg" alt="Opis" loading="lazy" width="1200" height="800">

Nie usypiaj obrazka z nagłówka

Obrazowi, który jest widoczny od razu (zwłaszcza temu „bohaterskiemu” z góry strony — Twojemu LCP), nie dawaj loading="lazy". Opóźnisz wtedy najważniejszy element i pogorszysz wynik. Lazy stosuj tylko poniżej pierwszego ekranu.

width i height — żeby strona nie skakała

Gdy <img> nie ma podanych wymiarów, przeglądarka nie wie, ile miejsca zarezerwować. Renderuje stronę, a po pobraniu obrazka cała treść nagle przeskakuje — to słynny CLS (skaczący układ). Lekarstwo: zawsze podawaj width i height (albo aspect-ratio w CSS). Przeglądarka rezerwuje miejsce z góry i nic nie skacze.

Responsywne obrazy — srcset i sizes

Telefon i monitor 4K nie potrzebują tego samego pliku. Po co wysyłać 1600 px na ekran o szerokości 400 px? Atrybuty srcset i sizes pozwalają przeglądarce samej wybrać najlepszy rozmiar dla danego urządzenia.

Diagram srcset — jedno źródło, różne rozmiary dla telefonu, tabletu i desktopu

W kodzie wygląda to tak:

<img
  src="zdjecie-800.jpg"
  srcset="zdjecie-400.jpg 400w,
          zdjecie-800.jpg 800w,
          zdjecie-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 600px"
  alt="Opis zdjecia"
  loading="lazy"
  width="1200"
  height="800">

Mówiąc po ludzku: srcset to lista dostępnych rozmiarów, a sizes mówi przeglądarce, jak duży będzie obraz na ekranie. Telefon pobierze wersję 400 px, tablet 800 px, a desktop 1200 px — każdy dostaje dokładnie tyle, ile potrzebuje, i ani bajta więcej.

Jeśli chcesz jeszcze dorzucić różne formaty (AVIF z fallbackiem do JPEG), użyj <picture>:

<picture>
  <source type="image/avif" srcset="zdjecie.avif">
  <source type="image/webp" srcset="zdjecie.webp">
  <img src="zdjecie.jpg" alt="Opis" loading="lazy" width="1200" height="800">
</picture>

CDN i Cloudflare Images

Ostatni element to dostarczanie obrazów blisko użytkownika. CDN trzyma kopie Twoich plików na serwerach rozsianych po świecie, więc nikt nie ściąga zdjęć z drugiego końca globu. Cloudflare (w darmowym planie) cache’uje statyczne obrazy automatycznie, a płatne Cloudflare Images potrafi przeskalować i przekonwertować obraz „w locie” pod konkretne urządzenie. O stawianiu Cloudflare przed stroną pisałem osobno: Cloudflare przed WordPressem za darmo.

Jak robi to Astro (i dlaczego warto)

Jeśli budujesz stronę na nowoczesnym frameworku, sporo z tego dostajesz za darmo. W Astro komponent <Image /> sam generuje wiele rozmiarów, konwertuje do AVIF/WebP, dodaje width/height i loading="lazy":

---
import { Image } from "astro:assets";
import zdjecie from "../assets/zdjecie.jpg";
---

<Image
  src={zdjecie}
  alt="Opis zdjecia"
  widths={[400, 800, 1200]}
  sizes="(max-width: 600px) 100vw, 600px"
/>

Z jednego pliku źródłowego Astro robi cały komplet zoptymalizowanych wariantów i wstawia poprawny srcset. To dokładnie ta robota, którą ręcznie opisałem wyżej — tylko zautomatyzowana podczas budowania strony.

Podsumowanie

Obrazy to najcięższa, a zarazem najłatwiejsza do naprawienia część strony. Zapamiętaj cztery kroki: przeskaluj do realnych wymiarów, przekonwertuj do AVIF/WebP, skompresuj i podaj wymiary plus loading="lazy" poniżej ekranu. Do tego srcset dla responsywności i CDN do dostarczania. To wszystko.

Jeśli przy okazji chcesz dobić do wysokiej oceny w testach, zerknij na Lighthouse 100/100 — czy warto i jak to ugryźć.

Masz stronę pełną ciężkich zdjęć i nie wiesz, od czego zacząć? Napisz do mnie — przejrzę grafikę i podpowiem, gdzie leży najwięcej zmarnowanych megabajtów.