Odruchowa odpowiedź na potrzebę dynamiki brzmi: „cała strona musi przejść na ”. To błąd kosztujący wydajność i budżet CDN. zachowują statyczny charakter większości strony, generując na żądanie tylko te fragmenty, które tego faktycznie wymagają.
Czym tak właściwie są Server Islands?
Najprościej rzecz ujmując, Server Island to komponent renderowany po stronie serwera, ale w sposób całkowicie niezależny od głównej strony. Jak to wygląda w praktyce? Pierwsza odpowiedź serwera dostarcza nam statyczny HTML, w którym znajduje się jedynie tzw. (czyli tymczasowe miejsce zastępcze) tam, gdzie docelowo ma pojawić się nasza dynamiczna treść. Chwilę później, w tle, maleńki skrypt wstrzyknięty przez Astro wysyła kolejne żądanie, pobiera gotową wyspę i płynnie podmienia wstawiony wcześniej placeholder.
Najważniejsza różnica między Server Islands a dobrze znanymi jest fundamentalna: wyspa serwerowa nie renderuje się w przeglądarce użytkownika. Cały proces zachodzi na serwerze i dzięki temu wyspa może bezpośrednio łączyć się z bazą danych, korzystać z bezpiecznych kluczy (sekretów), czytać ciasteczka i zarządzać sesjami — czyli może wszystko co klasyczny SSR. Przewaga polega na tym, że renderowanie wyspy nie opóźnia wysłania głównego szkieletu strony.
Kiedy Server Islands pokazują pazur?
Oto kilka życiowych przykładów, w których to rozwiązanie sprawdza się idealnie:
Scenariusz 1: Sklep internetowy (karta produktu) Znakomita większość informacji — takich jak opis, zdjęcia, specyfikacja techniczna czy opinie klientów — rzadko ulega zmianie, więc z powodzeniem może być statyczna. Z kolei stan magazynowy to element, który musi być zawsze aktualny. Zanim wprowadzono Server Islands, staliśmy przed trudnym wyborem: albo przerzucamy całą stronę na powolniejszy i droższy SSR, albo ryzykujemy pokazanie klientowi towaru, którego już nie ma w magazynie.
Scenariusz 2: Blog z panelem użytkownika Treść artykułu jest w 100% statyczna i bezpiecznie leży w cache'u. Jednak avatar i spersonalizowane menu nawigacyjne zalogowanego czytelnika w nagłówku strony zależą od sesji i muszą być generowane dynamicznie.
Scenariusz 3: Spersonalizowane rekomendacje na blogu Sam tekst wpisu jest taki sam dla wszystkich, ale sekcja „Polecane dla Ciebie” pod artykułem ma już unikalny charakter i dobiera treści na podstawie profilu lub historii przeglądania danego czytelnika.
We wszystkich tych sytuacjach Server Islands dają nam to, co najlepsze z obu światów: główna treść może być mocno cache'owana (np. z nagłówkiem Cache-Control: public, max-age=3600), a to, co wymaga aktualizacji na żywo, "dociera" do użytkownika chwilę później w formie osobnego zapytania.
Podstawowa konfiguracja
Zanim zaczniemy, coś bardzo ważnego - Server Islands do działania potrzebują adaptera SSR, a w Astro 6 możemy sięgnąć po dowolne wspierane rozwiązanie, na przykład Node, Vercel, Netlify czy Cloudflare:
Kolejny krok to po prostu dodanie do wybranego komponentu dyrektywy server:defer:
Zobaczmy, jak może wyglądać sam komponent takiej wyspy:
Jak widać, wewnątrz komponentu Server Island możemy swobodnie korzystać z dobrodziejstw środowiska SSR — mamy dostęp do Astro.cookies, Astro.request, możemy odpytywać bazy danych i wczytywać ukryte zmienne środowiskowe. Jednocześnie główna zawartość strony pozostaje nietknięta i wciąż może być błyskawicznie serwowana prosto z cache'a.
Fallback, czyli zadbajmy o odczucia użytkownika
Atrybut slot="fallback" to zdecydowanie coś więcej niż tylko estetyczny dodatek, ponieważ to kluczowy element, który ma realny wpływ zarówno na (doświadczenia użytkownika), jak i na wskaźniki . Jeśli go z jakiegoś powodu pominiemy, użytkownik najpierw zobaczy pustą dziurę na stronie, a ułamek sekundy później docelowa treść nagle "wskoczy" na swoje miejsce. Taki efekt powoduje nieprzyjemne przesunięcia układu strony (znane jako — Cumulative Layout Shift), a możesz mi wierzyć - algorytmy Google'a bardzo tego nie lubią i może to mieć jakiś wpływ na pozycje strony, nie mówiąc już o negatywnych skutkach dla użytkownika.
Dobra praktyka podpowiada proste rozwiązanie: nasz element zastępczy powinien mieć dokładnie takie same wymiary jak ostateczna zawartość wyspy:
Podobnie sprawa wygląda w przypadku tzw. skeletonów (ekranów szkieletowych):
A tak mógłby wyglądać przykładowy kod CSS dla takiego skeletonu (z użyciem frameworka Tailwind CSS):
Dzięki takiemu podejściu użytkownik od razu dostaje jasny sygnał, że dane wciąż się ładują, a układ strony pozostaje stabilny i nasze wskaźniki CLS utrzymują się na idealnym poziomie.
Jak to wygląda pod maską?
Cały proces renderowania strony z użyciem Server Islands w Astro przebiega w trzech prostych krokach:
- Każdy komponent oznaczony jako
server:deferzostaje wyizolowany i przypisany do specjalnej, ukrytej ścieżki (np./_server-islands/{nazwa}). - W wygenerowanym dokumencie HTML w miejscu komponentu ląduje wspomniany wcześniej placeholder oraz miniaturowy skrypt. Kiedy strona ładuje się w przeglądarce, skrypt ten natychmiast wysyła zapytanie (
fetch()) pod adres wygenerowanej wyspy. - Serwer zwraca gotowy kod HTML wyspy, który gładko zastępuje tymczasowy placeholder.
Z perspektywy przeglądarki przypomina to następujący scenariusz:
Warto zaznaczyć, że wszystkie (właściwości), które przekazujemy do wyspy, są automatycznie szyfrowane unikalnym kluczem (generowanym przy każdym budowaniu projektu) i doczepiane do adresu URL jako parametry. Co nam to daje?
- Możemy bezpiecznie przesyłać poufne dane (jak choćby wewnętrzne identyfikatory) bez obaw, że ktoś złośliwy ręcznie podmieni wartości w adresie.
- Astro domyślnie respektuje nagłówki
Cache-Control, więc jeśli wyspa jest wywoływana z dokładnie takimi samymi propsami, systemy takie jak Cloudflare mogą bez problemu zaserwować gotową odpowiedź prosto z pamięci podręcznej.
Należy jednak pamiętać o jednym istotnym ograniczeniu, by długość adresu URL nie przekraczała 2048 bajtów, ponieważ gdy zechcemy przekazać za dużo danych w propsach, Astro automatycznie zmieni typ zapytania na POST i to drastycznie utrudni późniejsze cache'owanie. Praktyczny wniosek jest taki, by przekazywać do wyspy wyłącznie najpotrzebniejsze identyfikatory, a całą resztę obszernych danych dociągać już wewnątrz samego komponentu.
Server Islands vs. Partial Prerendering w Next.js
Brzmi znajomo? Jeśli śledzisz nowości w świecie Next.js (szczególnie od wersji 15), z pewnością kojarzysz technologię (PPR) połączoną z mechanizmem (streaming) opartym na komponencie Suspense. Na papierze obie technologie rozwiązują bardzo zbliżony problem, jednak pod spodem działają całkowicie inaczej:
| Aspekt | Astro Server Islands | Next.js PPR |
|---|---|---|
| Model mentalny | Wyspa jako odseparowany endpoint HTTP | Granica Suspense w obrębie jednej strony |
| Dostawa | Drugie, asynchroniczne żądanie HTTP | Strumieniowanie danych w ramach pierwszego żądania |
| Środowisko | Lekki skrypt odpowiedzialny za podmianę HTML-a | Całe środowisko Reacta (runtime) + logika strumieniowania |
| Cache'owanie | Indywidualne, elastyczne reguły dla każdej wyspy | Cache nakładany całościowo (mniejsza kontrola nad detalami) |
| Kiedy wygrywa? | Treści statyczne urozmaicone małymi porcjami dynamicznych danych | Złożone aplikacje webowe opierające się mocno na architekturze SSR |
PPR jest głęboko wrośnięty w ekosystem Reacta i zdecydowanie lepiej odnajduje się w cięższych aplikacjach webowych. Z kolei Server Islands to rozwiązanie prostsze koncepcyjnie, które znakomicie wpisuje się w naturę stron zorientowanych na szybkie dostarczanie treści (portale czy blogi). Trudno tu wyłonić jednoznacznego zwycięzcę i lepiej go zresztą nie wyłaniać, ponieważ to po prostu dwa świetne, lecz odmienne narzędzia do osiągnięcia podobnego celu. Chcesz wiedzieć więcej? Zajrzyj do mojego artykułu porównującego fundamenty Astro i Next.js.
Tworzymy licznik komentarzy (przykład praktyczny)
Wyobraźmy sobie taki dość typowy scenariusz: prowadzimy bloga liczącego ponad setkę wpisów, a każdy z artykułów jest generowany statycznie i przez pełną godzinę siedzi bezpiecznie w cache'u CDN. Chcemy jednak, by licznik komentarzy pod wpisem zawsze pokazywał aktualny jego stan.
Gdybyśmy nie użyli Server Islands, prawdopodobnie użylibyśmy zwykłego fetch(), który odpytywałby API z poziomu głównego szablonu. Tyle, że cała strona przymusowo przechodzi na SSR i finalnie bardzo trudno jest ją optymalnie buforować (bo w końcu komentarze ciągle się pojawiają), a sam czas wczytywania ulega wydłużeniu.
Z pomocą Server Islands rozwiązujemy ten problem niezwykle elegancko:
Co dzięki temu zyskujemy? Główny artykuł wciąż bezpiecznie "siedzi" w cache'u CDN przez całą godzinę, podczas gdy licznik komentarzy albo odświeża się z każdym odwiedzeniem strony, albo korzysta z osobnego - własnoręcznie ustawionego - buforowania dla danej wyspy.
O czym warto pamiętać? (Ograniczenia i pułapki)
Każda technologia ma swoje własne zasady o których warto wiedzieć od razu:
1. Propsy muszą być bezwzględnie serializowalne. Do wnętrza Server Island nie wrzucisz funkcji, instancji klas ani złożonych komponentów. Musisz cały czas działać na podstawowych formatach danych, czyli typach prostych, tablicach, obiektach JSON czy datach.
2. Uważaj na obiekt Astro.url. Jeśli go wywołasz wewnątrz komponentu wyspy, nie pokaże Ci adresu bazowego (np. /blog/moj-wpis), ale wewnętrzną ścieżkę prowadzącą do wyspy (np. /_server-islands/CommentsCount?...). Potrzebujesz adresu głównej strony? Pobierz go z nagłówka Referer:
3. Wyspy żyją we własnym świecie. Renderują się w całkowitym oderwaniu od reszty drzewa i jeśli na poziomie głównego dokumentu ustawisz jakąś zmienną kontekstową, Twoja wyspa nie będzie miała do niej dostępu. Pamiętaj, że komunikacja musi przebiegać wprost przez zadeklarowane propsy.
4. Uważaj na buforowanie prywatnych danych. Jeśli implementujesz wyspę, która pobiera informacje ściśle przypisane do konkretnego użytkownika (np. wspomniany avatar po zalogowaniu), bezwzględnie zadbaj o dodanie nagłówków Cache-Control: private lub no-store. Brak tej konfiguracji może poskutkować tym, że serwer CDN przypadkowo wyświetli poufne dane innej osobie odwiedzającej Twój serwis.
Dobre praktyki: buforowanie, prywatność i placeholdery
Z Server Islands wyciśniesz maksimum możliwości, pod warunkiem że nauczysz się sprawnie zarządzać trzema odrębnymi warstwami: głównym szkieletem HTML, dynamicznym punktem końcowym samej wyspy oraz jej stanem zastępczym.
Oto złote zasady, których powinieneś się ZAWSZE trzymać:
- Ponieważ główna treść zazwyczaj nie zawiera prywatnych informacji, śmiało nakładaj na nią bardzo rygorystyczne zasady publicznego cache'owania.
- Dla wysp mających poufne dane (tylko do wglądu dla danego użytkownika) zawsze stosuj profil zapobiegawczy —
Cache-Control: privatealbono-store. - Kiedy operujesz na danych ogólnodostępnych, ale podlegających częstym rotacjom (jak np. stany magazynowe w sklepie), ustaw dla wyspy bardzo krótki czas trwania pamięci podręcznej (np. ).
- Oszczędzaj bajty, czyli w praktyce - przekazuj wyspie jedynie bazowe identyfikatory i niezbędne minimum informacji, zamiast wrzucać do propsów całe, masywne obiekty pobrane uprzednio z bazy.
- Nie zapominaj także o precyzyjnych wymiarach dla swoich placeholderów, aby uniknąć niepotrzebnego "skakania" elementów na stronie (ma to negatywny wpływ na metryki CLS).
Wydajność — czy to się w ogóle opłaca?
Zazwyczaj tak, aczkolwiek bywają wyjątki od reguły. Server Islands rozwijają skrzydła i pokazują pełen potencjał w różnych sytuacjach:
- Miażdząca większość zawartości strony jest w pełni statyczna, co pozwala na jej drastyczne optymalizowanie za pomocą cache'a.
- Dodawany fragment dynamiczny jest na tyle niewielki, że wygenerowanie drugiego, docelowego żądania odbywa się praktycznie w oka mgnieniu.
- Twoja domena obsługuje zauważalny ruch, przez co korzyści płynące z cache'owania na poziomie CDN stają się łatwo mierzalne.
Trzeba jednak wiedzieć, że to rozwiązanie mija się z celem, gdy:
- Cały Twój projekt ze swej natury (np. zamknięty dashboard dla klientów) opiera się wyłącznie na procesach (SSR).
- Dynamiczna zawartość to w rzeczywistości ogromny moloch (np. paginowana, długa na 50 pozycji sekcja z wynikami wyszukiwania), a w takich przypadkach wymóg wykonania asynchronicznego żądania będzie w praktyce wąskim gardłem i spowolni wczytywanie aplikacji.
- Prowadzisz malutką stronę o bardzo niskim natężeniu ruchu, gdzie stosowanie skomplikowanych wdrożeń z zakresu optymalizacji CDN przynosi mikroskopijne zyski.
