Mamy stronę usługową w Astro: hero z obrazem, sekcje oferty, dowód społeczny, formularz kontaktowy jako wyspa React, analityka i czat. Świeży build dał 94/100 na mobile — wysoko, bo Astro nie wysyła zbędnego JavaScriptu. Ale klient chciał komplet. wskazał obszary do domknięcia: obrazy, fonty, skrypty third-party, selektywną hydratację, dostępność i konfigurację produkcyjnego hostingu.
- Lighthouse Mobile po optymalizacji
- 94 → 100Lighthouse Mobile po optymalizacji
- LCP po uporządkowaniu obrazu hero
- 2.1 s → 0.9 sLCP po uporządkowaniu obrazu hero
- CLS po wdrożeniu Fonts API
- 0.09 → 0.00CLS po wdrożeniu Fonts API
W nowszych raportach Lighthouse warto sprawdzać też Przeglądanie Agentowe. Astro ma tu naturalną przewagę, bo statyczny HTML, semantyka i niski CLS są dokładnie tym, co pomaga zarówno ludziom, jak i agentom AI. WebMCP zostawiłbym na projekty transakcyjne, ale llms.txt i stabilny layout są tanimi wygranymi.
Dlaczego Astro startuje wysoko w Lighthouse
Zanim zaczniemy optymalizować, warto zrozumieć, skąd te 94 punkty na starcie. Astro renderuje stronę jako statyczny HTML i domyślnie nie wysyła do przeglądarki frameworkowego JavaScriptu. Sekcje takie jak hero, oferta czy stopka to komponenty .astro, czyli czysty HTML i zero kosztu po stronie klienta.
To strukturalna przewaga nad aplikacyjnym modelem React/Next, gdzie nawet statyczna strona ładuje runtime frameworka. Szerzej rozkładam to w artykule Astro.js vs Next.js. Dla Lighthouse oznacza to niski i dobry punkt wyjścia pod bez żadnej pracy.
Ostatnie punkty to nie walka z frameworkiem — to dopracowanie zasobów, które framework przepuszcza bez zmian: obrazów, fontów i skryptów zewnętrznych.
Krok 1: Obrazy — astro:assets zamiast surowego <img>
Największy problem był w hero. Obraz wstawiony jako surowy <img> ładował się w pełnej wadze, bez WebP/AVIF i bez wymiarów. Efektem był duży i przesunięcia układu.
Rozwiązaniem jest komponent <Image> z :
Trzy rzeczy zadziałały naraz. WebP/AVIF generuje się automatycznie podczas builda, bez ręcznej obróbki ani pipeline'u graficznego. Wymiary width i height sprawiają, że przeglądarka rezerwuje miejsce jeszcze przed załadowaniem obrazu, więc układ nie skacze. loading="eager" z fetchpriority="high" wyciąga obraz hero poza kolejkę i ładuje go jako pierwszy — bo to on jest elementem LCP, który Lighthouse mierzy. W efekcie LCP spadł z 2.1 s do 0.9 s, a pozostałe obrazy dostały loading="lazy" automatycznie.
Krok 2: Fonty — Fonts API zamiast Google Fonts z CDN
Drugim źródłem były Google Fonts ładowane z zewnętrznego : zewnętrzny request, FOUT i skok układu, gdy właściwy krój się doczytał.
Rozwiązaniem jest Fonts ustabilizowane w Astro 6 (top-level klucz fonts), które hostuje krój lokalnie i generuje dopasowane fallbacki:
Astro podczas builda pobiera krój, kopiuje go do lokalnych zasobów, generuje font-display: swap z fallbackiem o zbliżonych metrykach i dodaje preload. Produkcja nie odpytuje serwerów Google, a tekst nie skacze przy podmianie kroju. CLS z fontów spadł z 0.09 do 0.00. Dla polskich znaków kluczowy jest latin-ext w subsets.
Krok 3: Skrypty third-party — leniwe ładowanie
Analityka i widget czatu ładowały się synchronicznie, blokując główny wątek i podbijając TBT. W Astro skrypty kontrolujesz dyrektywami i atrybutami ładowania:
Dla cięższych integracji (czat, embedy) najlepszą strategią jest załadowanie ich dopiero po interakcji albo gdy wejdą w viewport — przez wyspę z client:visible albo client:idle, zamiast wpinać skrypt w <head>. Wszystko, co nie jest krytyczne dla pierwszego renderu, schodzi poza ścieżkę krytyczną.
W wyniku tych działań TBT spadł poniżej progu, a INP ustabilizował się na bardzo niskim poziomie.
Krok 4: Dyrektywy client — selektywna hydratacja
To najważniejsza dźwignia specyficzna dla Astro. Formularz kontaktowy był wyspą React z client:load — hydratował się natychmiast, mimo że użytkownik dociera do niego dopiero po przewinięciu strony.
Zmiana na client:visible:
Każdy client:load to JavaScript blokujący wątek od razu po załadowaniu. client:visible odkłada koszt do momentu, gdy komponent jest realnie potrzebny — a jeśli użytkownik nie doscrolluje, kod nie pobiera się wcale. To główny mechanizm, którym w Astro sterujesz INP i TBT. Szerzej rozkładam dyrektywy w osobnym artykule.
Reguła: client:load tylko dla interfejsu krytycznego na pierwszym ekranie. Wszystko poniżej — client:visible.
Krok 5: Usuń wyspy tam, gdzie wystarczy HTML, CSS i View Transitions
Najłatwiejszy JavaScript do optymalizacji to taki, którego w ogóle nie wysyłamy do przeglądarki. W Astro warto więc przejrzeć wszystkie wyspy React, Vue albo Svelte i dokładnie przemyśleć, czy dany element naprawdę potrzebuje stanu po stronie klienta?
Prosty hamburger, rozwijana sekcja FAQ, podstawowa animacja wejścia, hover, przejście między podstronami albo zakładki w małej sekcji często nie wymagają frameworka. Wystarczy semantyczny HTML, CSS, ewentualnie kilka linii natywnego JavaScriptu albo View Transitions:
W tym wszystkim, chodzi o brak tam, gdzie nie ma realnej interaktywności. W sytuacji, kiedy komponent tylko pokazuje i chowa treść, animuje kartę albo zmienia stan wizualny, zacznij od HTML/CSS. Wyspę frameworka zostaw dla formularzy, konfiguratorów, map, kalkulatorów i elementów, które naprawdę mają logikę po stronie klienta.
Krok 6: Dostępność i SEO — łatwe punkty, które często uciekają
Lighthouse Performance to tylko jedna kategoria, bo jeśli celujesz w komplet wyników, musisz domknąć też Accessibility i SEO, a w 2026 roku pośrednio również czytelność dla agentów AI.
Minimum, które sprawdzam przy stronie Astro:
Jeden czytelny
h1na stronie i logiczna hierarchia nagłówków bez skoków zh2doh4.Kontrast tekstu zgodny z WCAG, szczególnie na przyciskach, kartach i jasnych gradientach.
Atrybuty
altdla obrazów niosących informację; pustealt=""tylko dla dekoracji.Prawdziwe
<button>dla akcji i<a>dla przejść, zamiast klikalnychdiv-ów.Unikalne
title,description, canonical i Open Graph dla każdej ważnej podstrony.
W Astro często robię to przez wspólny komponent layoutu albo BaseHead.astro, który wymusza metadane i nie pozwala zapomnieć o adresie kanonicznym. To nie podbija samego Performance, ale domyka Lighthouse SEO, poprawia podglądy social media i daje agentom AI czytelniejszą strukturę dokumentu.
Krok 7: SSG, cache i CDN — produkcja też jest częścią wyniku
Astro domyślnie świetnie nadaje się do stron statycznych: blogów, portfolio, dokumentacji i stron usługowych. Jeśli nie potrzebujesz renderowania na żądanie, trzymaj projekt w trybie . Wtedy podczas builda powstają statyczne pliki HTML, CSS i JS, które CDN może serwować z brzegu sieci bez zimnych startów i bez pracy serwera przy każdym wejściu.
To ma bezpośredni wpływ na . Lighthouse lokalnie nie pokaże całej prawdy, bo prawdziwy wynik zależy od hostingu, kompresji i cache. Na produkcji sprawdź:
HTML serwowany z krótkim, kontrolowanym cache albo z mechanizmem purge po deployu.
Assety z hashem w nazwie, np. CSS, JS, fonty i obrazy, z długim
Cache-Control: public, max-age=31536000, immutable.Kompresję Brotli albo Gzip dla HTML, CSS, JS, SVG i plików tekstowych.
CDN, np. Cloudflare, Netlify, Vercel albo Cloudflare Pages, zamiast serwowania statycznej strony z jednego odległego serwera.
Test PageSpeed Insights na produkcyjnym URL po deployu, nie tylko Lighthouse w trybie dev.
Same techniki optymalizacji pozostają niezmienne, poczynając od kompresji grafik i fontów, po leniwe ładowanie skryptów. Różnica tkwi w punkcie wyjścia: Next.js na starcie niesie ze sobą narzut w postaci runtime'u Reacta, podczas gdy Astro domyślnie nie serwuje żadnego kodu JavaScript. Ostatecznie osiągnięcie wyniku 100/100 na obu platformach wymaga dokładnie tej samej dyscypliny technicznej.
Wynik końcowy optymalizacji Lighthouse 100/100 w Astro
| Metryka | Przed | Po |
|---|---|---|
| Lighthouse Mobile | 94 | 100 |
| Lighthouse Desktop | 98 | 100 |
| LCP | 2.1 s | 0.9 s |
| INP | 90 ms | 40 ms |
| CLS | 0.09 | 0.00 |
| TBT | 180 ms | 30 ms |
Lista kontrolna optymalizacji Astro pod Lighthouse i Core Web Vitals
Dziewięć punktów, które w tym case study dały największy efekt i które warto wbudować w każdy projekt Astro od początku:
- Obrazy przez
astro:assets.<Image>zwidth/height,loading="eager"ifetchpriority="high"na elemencie LCP. - Fonty przez Fonts API (Astro 6) z
latin-extdla polskiego — samodzielne hostowanie zamiast Google Fonts z CDN eliminuje FOUT i layout shift. client:visiblejako domyślna dyrektywa;client:loadtylko dla interfejsu krytycznego na pierwszym ekranie.- Proste interakcje najpierw HTML/CSS/View Transitions, a dopiero potem wyspa React, Vue albo Svelte.
- Skrypty third-party
async/deferalbo jako wyspaclient:idle/client:visible, a nigdy synchronicznie w<head>. - Semantyczne nagłówki, kontrast,
altdla obrazów i prawdziwe linki oraz przyciski zamiast klikalnychdiv-ów. - Unikalne metadane SEO:
title,description, canonical i Open Graph dla każdej ważnej podstrony. - SSG dla stron treściowych oraz statyczne assety z długim cache na CDN.
- Testuj na produkcyjnym URL przez PageSpeed Insights, nie tylko lokalnie, ponieważ Lighthouse w dev nie mierzy TTFB ani opóźnień CDN.
Astro vs Next.js, czyli inny start, ale ten sam cel.
Ten case study jest świadomym bliźniakiem mojego case study 100/100 w Next.js. Różnica nie leży w technikach: obrazy, fonty, lazy loading skryptów obowiązują tak samo. Leży w punkcie startu: Next.js zaczyna niżej (runtime React do okiełznania), Astro wyżej (zero JS domyślnie), ale obie strony dochodzą do 100/100 tą samą dyscypliną.
