Dyrektywy hydracji (client:*) to jedno z najważniejszych narzędzi w Astro — i jednocześnie jedno z tych, które początkujący najczęściej stosują niewłaściwie. Różnica między client:load a client:visible to nie jest kwestia kosmetyczna; ma ona realny wpływ na metryki Core Web Vitals oraz odczucia użytkownika (User Experience).
W tym artykule rozbieram każdą z pięciu dyrektyw, pokazuję, kiedy której użyć i jakie błędy najczęściej spotykam w audytach stron Astro.
Kontekst — po co w ogóle dyrektywy hydracji
Astro domyślnie wysyła do przeglądarki zero JavaScriptu. Jeśli komponent (React, Vue, Svelte) ma być interaktywny, musisz jawnie wskazać, kiedy i jak ma pobrać swój kod JS. To świadoma decyzja projektowa — nie ma domyślnego „wszystko hydratuj", bo to by niszczyło całą ideę architektury wysp.
Jeżeli nie wiesz, czym są wyspy w Astro, zacznij od wpisu o Islands Architecture.
Dyrektywa Dyrektywy client:* w Astro określają, kiedy komponent frameworkowy ma pobrać kod JavaScript i stać się interaktywny w przeglądarce. to kontrakt wydajnościowy — mówisz frameworkowi: „ten komponent otrzyma JS w taki a taki sposób". Astro renderuje wszystko najpierw do statycznego HTML, a dyrektywa decyduje, kiedy (lub czy w ogóle) ten HTML zostanie ożywiony przez JavaScript po stronie klienta.
client:load — hydracja natychmiast
client:load pobiera i uruchamia kod JavaScript komponentu jak najszybciej — gdy tylko przeglądarka przetworzy HTML. To najbardziej agresywna dyrektywa i powinna być zarezerwowana dla elementów, które muszą działać od razu.
Kiedy używać:
- Nawigacja główna z rozwijanym menu, które użytkownik prawdopodobnie otworzy natychmiast.
- Przycisk CTA w głównej sekcji (hero), który musi otworzyć okno modalne.
- Komponent do zmiany motywu (ciemny/jasny), który ma zareagować na preferencje użytkownika bez migotania strony.
Kiedy NIE używać:
- Formularze kontaktowe w stopce — do nich użytkownik musi przewinąć stronę, więc
client:visiblew zupełności wystarczy. - Komentarze pod wpisem — z tego samego powodu co wyżej.
- Galeria zdjęć, którą widać dopiero po kliknięciu w miniaturkę.
Koszt użycia client:load to dodatkowe kilobajty wpływające negatywnie na metryki Total Blocking Time (TBT) i Time to Interactive (TTI). Jeśli bez zastanowienia oznaczysz wszystkie wyspy jako client:load, stracisz całą przewagę wydajnościową Astro.
client:idle — hydracja, kiedy przeglądarka jest bezczynna
client:idle czeka na wywołanie requestIdleCallback — moment, w którym przeglądarka zakończyła krytyczne zadania i procesor ma wolny cykl. W praktyce następuje to zazwyczaj od kilkuset milisekund do sekundy po załadowaniu strony.
Kiedy używać:
- Komponenty ważne, ale nie krytyczne dla pierwszej interakcji — zapis na newsletter, widżet czatu, baner zgody na pliki cookie (uwaga: RODO wymaga, by baner pojawił się przed załadowaniem innych skryptów śledzących).
- Skrypty analityczne i widżety dostawców zewnętrznych, które wpływają na interaktywność, ale nie stanowią priorytetu.
- Przyciski udostępniania w mediach społecznościowych.
Kiedy NIE używać:
- Elementy znajdujące się poza początkowo widocznym obszarem strony, których załadowanie można odłożyć używając
client:visible. - Krytyczne elementy interfejsu (okna modalne, główna nawigacja).
Różnica w czasie załadowania między client:idle a client:load to zwykle od 100 do 500 ms, jednak to właśnie te ułamki sekund mają decydujący wpływ na TTI oraz wynik wydajności w Lighthouse.
client:visible — hydracja, kiedy komponent wchodzi w pole widzenia
client:visible wykorzystuje API IntersectionObserver, aby pobrać i uruchomić JavaScript dopiero wtedy, gdy komponent pojawi się w widocznym obszarze ekranu. Jeśli użytkownik nie przewinie strony do tej sekcji — kod JS w ogóle się nie pobierze.
To najbardziej oszczędna dyrektywa i moja domyślna rekomendacja dla wszystkich elementów znajdujących się poza pierwszym ekranem (poniżej linii przewijania).
Kiedy używać:
- Sekcja komentarzy pod artykułem.
- Formularz kontaktowy w stopce lub na samym dole strony.
- Galeria zdjęć znajdująca się poniżej opisu produktu.
- Kalkulator lub interaktywny widżet osadzony w środku wpisu.
- Karuzela z logotypami klientów.
Kiedy NIE używać:
- Komponenty na pierwszym ekranie — są one widoczne od razu, więc użycie
client:visibleniczego nie optymalizuje, a jedynie dokłada dodatkowy narzut związany z obserwatorem (IntersectionObserver). - Komponenty współdzielące stan z innymi wyspami — mechanizm obserwacji może wprowadzić zauważalne opóźnienie w synchronizacji danych.
Możesz też skonfigurować parametr client:visible, aby rozpocząć pobieranie kodu JS zanim komponent fizycznie wejdzie w widoczny obszar (prefetching):
Dzięki temu kod ładuje się, gdy komponent znajduje się np. 200 pikseli poniżej aktualnego widoku — użytkownik nie odczuje żadnego opóźnienia, a w przypadku gdy w ogóle tam nie przewinie, oszczędzamy zasoby sieciowe.
client:media — hydracja tylko przy spełnieniu warunków media query
client:media ożywia komponent tylko wtedy, gdy przeglądarka spełnia przekazane reguły zapytania o media (media query). Na smartfonie DesktopSidebar nigdy nie pobierze swojego kodu JS, podobnie jak MobileNav na komputerze stacjonarnym.
Kiedy używać:
- Elementy interfejsu przeznaczone wyłącznie na urządzenia mobilne — menu typu hamburger, wysuwane panele dolne (bottom sheets), galerie obsługujące gesty przesunięcia (swipe).
- Elementy wyłącznie desktopowe — rozbudowane menu rozwijane po najechaniu kursorem, skomplikowane filtry wykorzystujące mechanizm przeciągnij i upuść.
- Responsywne widżety, które mają kompletnie inną logikę działania w zależności od rozmiaru ekranu.
Kiedy NIE używać:
- Kiedy komponent dostosowuje się tylko wizualnie (za pomocą CSS media queries), ale nie zmienia logiki biznesowej.
- Gdy zachowanie różni się zaledwie szczegółem — prościej jest użyć
client:visiblei wewnątrz komponentu weryfikować warunki przezwindow.matchMedia.
Jest to dyrektywa, po którą sięgam stosunkowo rzadko, lecz w specyficznych sytuacjach (np. przy ważącym dużo wysuwanym panelu na telefonach, który w ogóle nie jest renderowany na desktopie) pozwala zaoszczędzić dziesiątki kilobajtów transferu.
client:only — komponent wyłącznie po stronie klienta
client:only to sygnał dla Astro: „nie próbuj renderować tego komponentu po stronie serwera (SSR), wygeneruj go wyłącznie w przeglądarce". Działa to odwrotnie do pozostałych dyrektyw.
Kiedy używać:
- Komponenty zależne w fazie renderowania od obiektów globalnych przeglądarki takich jak
window,documentczylocalStorage— bez tego proces SSR zakończyłby się błędem. - Biblioteki, które nie posiadają wsparcia dla SSR (np. niektóre nakładki na Three.js, react-leaflet, edytory tekstu typu WYSIWYG).
- Komponenty bazujące na danych z bazy
IndexedDBlub service workerów. - Elementy, których wygląd jest bardzo mocno uzależniony od dokładnego rozmiaru okna, a wersja wyrenderowana na serwerze i tak wymagałaby natychmiastowego przebudowania przez JS.
Kiedy NIE używać:
- We wszystkich innych przypadkach. Użycie
client:onlydezaktywuje mechanizm SSR, przez co rezygnujesz z błyskawicznego serwowania gotowej treści. Zamiast tego zmuszasz użytkownika do oczekiwania, aż pobierze się JS i wygeneruje komponent bezpośrednio w jego przeglądarce.
Sprawdzoną praktyką przy korzystaniu z client:only jest dodanie zastępczego szkieletu (placeholdera):
Dzięki temu zabiegowi użytkownik na czas wczytywania widzi zarezerwowaną przestrzeń, układ strony nie „skacze", a wskaźnik przesunięcia układu (CLS) pozostaje nienaruszony.
Porównanie w tabeli
| Dyrektywa | Kiedy ładuje JS | Kiedy używać | Kiedy unikać |
|---|---|---|---|
client:load | Natychmiast | Krytyczne interakcje na pierwszym ekranie (nawigacja, główne przyciski) | Elementy widoczne dopiero po przewinięciu |
client:idle | Gdy przeglądarka jest bezczynna | Ważne, ale nie priorytetowe elementy (newsletter, czat) | Krytyczny interfejs, komponenty reagujące na przewijanie |
client:visible | Gdy element wchodzi w pole widzenia | Wszystko poniżej pierwszego ekranu | Elementy widoczne od razu po załadowaniu |
client:media | Gdy spełnione są reguły media query | Interfejsy specyficzne tylko dla mobile / desktop | Gdy zmienia się wyłącznie wygląd, a nie logika |
client:only | Po załadowaniu strony (omija SSR) | Biblioteki niekompatybilne z SSR, wymagające okna przeglądarki | Każdy element, który można bez problemu wyrenderować na serwerze |
Budżet hydracji
W większych projektach wprowadzam limit wielkości wczytywanego kodu (budżet hydracji) dla każdego z typów komponentów. Takie podejście chroni przed sytuacją, w której strona na Astro teoretycznie powinna być szybka, a w praktyce i tak ładuje kilka masywnych paczek kodu.
- Komponenty widoczne od razu po załadowaniu: dozwolona maksymalnie 1-2 interaktywne wyspy z dyrektywą
client:load. - Sekcje widoczne po przewinięciu strony: zawsze domyślne użycie
client:visible. - Detale wizualne i dekoracje: całkowity brak hydracji, chyba że ewidentnie wpływają one na lepszą konwersję.
- Komponenty wymagające API przeglądarki:
client:only, z zaznaczeniem, że brak SSR jest w tym przypadku nie do ominięcia. - Współdzielone biblioteki: regularna weryfikacja, czy jeden niewielki komponent nagle nie importuje obszernej biblioteki do rysowania wykresów, edytora tekstu czy ogromnego zestawu gotowych elementów interfejsu (UI kit).
Najczęstsze błędy
W trakcie audytów witryn stworzonych w Astro notorycznie spotykam trzy problemy:
1. Nadużywanie client:load. Najczęstsze wytłumaczenie: „dzięki temu po prostu działa". To jednocześnie najszybsza droga do pogrzebania wysokiej wydajności. Serwis internetowy z ośmioma komponentami oznaczonymi client:load traci przewagę nad stronami opartymi np. o Next.js, stając się identycznym tworem, tyle że w innym ekosystemie.
2. Obejmowanie całego układu (layoutu) w jeden wielki React z client:load. Programista pracujący wcześniej głównie z Next.js odruchowo buduje bazowy plik Layout.tsx, tam importuje nagłówek, główną treść, stopkę i dodaje parametr client:load do całości. Fundamentem każdej strony powinien być szablon w czystym .astro, a frameworkowe komponenty należy dorzucać jak przyprawy – wyłącznie w miejscach autentycznej interakcji.
3. Pozostawienie client:only bez elementu zastępczego. Komponent wczytuje się dopiero po półtorej sekundy opóźnienia, co wywołuje skok całej reszty strony i tym samym wskaźnik CLS drastycznie rośnie. Pamiętaj, aby zawsze ustawiać kontener zastępczy zachowujący odpowiednią wysokość na czas załadowania.
Scenariusz praktyczny — karta produktu w e-commerce
Przeanalizujmy dobór odpowiednich dyrektyw przy projektowaniu standardowej podstrony produktowej:
Taka konfiguracja gwarantuje, że:
- Otrzymujemy natychmiastowe wyświetlenie pełnej struktury strony (bez oczekiwania na pobranie skryptów).
- Krytyczne strefy interfejsu (galeria zdjęć, przycisk zakupu) są błyskawicznie hydratowane i gotowe do akcji.
- Skrypty opinii ładują się tylko i wyłącznie, gdy użytkownik przewinie stronę do danej sekcji.
- Elementy o niższym priorytecie (jak karuzela sugerująca inne produkty) nie opóźniają pierwszego renderowania zawartości na ekranie.
Audyt własnej strony Astro
Jeśli rozwijasz już projekt korzystając z tego frameworka, polecam wykonać prosty audyt:
- Przeszukaj kod pod kątem
client:loadi upewnij się, czy każda taka deklaracja rzeczywiście jest nieodzowna na pierwszym ekranie. - Zmień na
client:visiblekażdą dyrektywę dla elementów znajdujących się poza pierwszym ekranem. - Poszukaj miejsc wykorzystania
client:only– być może część dałoby się wyrenderować przez SSR. Użycie tej dyrektywy powinno być udokumentowane (np. komentarzem „biblioteka X nie wspiera SSR"). - Uruchom narzędzie Lighthouse (na urządzeniach mobilnych). Wynik TBT powinien znajdować się poniżej 200 ms, a czas TTI wynosić maksymalnie 3,5 s na spowolnionym łączu 4G.
Jeśli chcesz zlecić profesjonalny audyt SEO i wydajności Twojej witryny oparty o technologię Astro — chętnie podejmę się tego zadania.
Podsumowanie
Dyrektywy client:* to precyzyjne narzędzie do optymalizacji wydajności. Nie istnieje jedna idealna, domyślna dyrektywa — każda z nich rozwiązuje inny problem i wiąże się z innymi kosztami. Praktyczna zasada, której sam się trzymam, to traktowanie client:visible jako domyślnego wyboru, używanie client:idle dla ważnych, lecz niepilnych elementów na pierwszym ekranie, client:load dla interfejsu krytycznego, a client:only w sytuacjach, gdy wsparcie dla renderowania po stronie serwera jest fizycznie niemożliwe.
Jeśli nie masz pewności, czy podział Twoich komponentów na wyspy jest optymalny, napisz do mnie — chętnie rzucę okiem na architekturę projektu i wskażę obszary do poprawy.
