Dla stron marketingowych, blogów, landing page i serwisów opartych na treści liczą się trzy rzeczy: szybkość, indeksowalny HTML i formularze, które nie czekają na kliencki JavaScript.
Dlaczego server-first w Next.js stał się domyślnym podejściem?
Wiele aplikacji React przesuwało pobieranie danych do useEffect, przez co
początkowa odpowiedź zawierała pustą powłokę oraz komunikat ładowania. Googlebot
potrafi wykonać JavaScript, ale inne roboty mogą go ignorować, a użytkownik na
słabszym urządzeniu nadal płaci za pobranie, parsowanie i wykonanie kodu.
W App Routerze punkt ciężkości wrócił na serwer. W praktyce oznacza to, że:
Komponenty renderują się na serwerze, jeśli nie oznaczysz ich jako klientowe
JavaScript trafia do przeglądarki tylko tam, gdzie jest faktycznie potrzebny (Client Components z dyrektywą
'use client')Formularze i mutacje danych obsługuje serwer bezpośrednio (Server Actions)
Publiczna treść może znaleźć się w odpowiedzi HTML bez pobierania jej przez efekt uruchamiany po
To domyślny model komponentów w App Routerze, a nie gotowa strategia renderowania. RSC nie oznacza automatycznie statycznej strony. Server Component może zostać wyrenderowany podczas builda, z cache albo dla każdego żądania. Decydują o tym użyte dane, dynamiczne API i konfiguracja Cache Components.
React Server Components: jak działają i jak wpływają na wydajność?
Jak React Server Components działają pod maską?
W aplikacji renderowanej wyłącznie po stronie klienta komponenty i pobieranie danych należą do pakietu JavaScript. SSR potrafił wcześniej dostarczyć HTML, lecz kod komponentów nadal trafiał do przeglądarki w celu hydratacji. RSC dodają inną granicę: kod Server Componentu pozostaje na serwerze.
W modelu RSC:
- Serwer tworzy specjalny format RSC Payload z wynikiem Server Components i odwołaniami do komponentów klienckich.
- Pierwsze wejście otrzymuje również HTML, który pokazuje nieinteraktywny podgląd trasy przed hydratacją.
- Client Components odzyskują interaktywność przez hydratację, korzystając ze swojego kodu JavaScript.
- Kolejna nawigacja używa głównie payloadu RSC, prefetchu i przejścia po stronie klienta, bez pełnego przeładowania dokumentu.
Wpływ React Server Components na Core Web Vitals
Core Web Vitals poprawią się wtedy, gdy nowa architektura faktycznie ograniczy pracę przeglądarki, skróci dostęp do głównej treści i ustabilizuje układ. Oceniaj 75. percentyl wizyt osobno dla urządzeń mobilnych i desktopowych.
Largest Contentful Paint (LCP)
mierzy czas do wyrenderowania największego elementu w widoku. RSC mogą pomóc, gdy element LCP i potrzebne mu dane powstają bez oczekiwania na klientowy fetch. Wolna odpowiedź serwera, nieoptymalny obraz lub umieszczenie hero za granicą Suspense zniwelują tę przewagę. Próg dobrego wyniku to maksymalnie 2,5 sekundy.
Interaction to Next Paint (INP)
mierzy opóźnienie między interakcją a następnym wyrenderowaniem. Mniejszy pakiet klienta może zostawić więcej czasu głównego wątku na obsługę zdarzeń. Ciężki Client Component, rozbudowany skrypt analityczny lub widget zewnętrzny nadal może przekroczyć próg 200 milisekund.
Cumulative Layout Shift (CLS)
CLS mierzy niestabilność układu. Render serwerowy nie zna automatycznie wymiarów obrazów, reklam i osadzonych widgetów. Rezerwuj miejsce w HTML i projektuj fallback Suspense o docelowych wymiarach. Dobry wynik nie przekracza 0,1.
Przykład React Server Components w sekcji blogowej
Rozważmy stronę blogową z listą artykułów i panelem kategorii. W wariancie renderowanym po stronie klienta:
Początkowy HTML tego wariantu nie zawiera artykułów. Użytkownik czeka na hydratację i kolejne żądanie, a robot bez obsługi JavaScriptu nie odczyta listy. Googlebot może ją zobaczyć po renderowaniu, lecz strona tworzy dodatkową zależność oraz później odkrywa treść i linki.
W podejściu server-first z RSC:
Pole nie potrzebuje useState, bo formularz przekazuje jego wartość przez
FormData. useActionState dostarcza wynik i stan oczekiwania. Trzeci argument,
czyli permalink, wskazuje adres używany przed załadowaniem JavaScriptu. Strona
docelowa musi wyrenderować ten sam formularz, tę samą akcję i ten sam permalink,
aby React przeniósł stan odpowiedzi. Bez permalinka Client Component kolejkuje
wysłanie przed hydratacją i odtwarza je po załadowaniu kodu.
Formularz serwerowy daje prostsze progressive enhancement. Dodawaj Client Component dopiero wtedy, gdy potrzebujesz stanu oczekiwania lub reakcji bez przeładowania. Więcej wzorców opisuję w artykule o React 19 Actions.
Efekt: przeglądarka może otrzymać kompletny HTML z listą artykułów bez czekania na pobieranie danych i renderowanie po stronie klienta. JavaScript ogranicza się do formularza newslettera. Taka architektura daje dobre warunki do poprawy LCP i ograniczenia ryzyka CLS, ale wynik trzeba potwierdzić pomiarem na realnej stronie: obraz LCP, fonty, CSS, odpowiedź API i hosting nadal mają znaczenie.
Server Actions w Next.js: mniej endpointów dla formularzy
Server Actions to funkcje serwerowe oznaczone dyrektywą 'use server', które możesz wywołać z komponentów React. Nie kasują całego API z projektu, ale zdejmują sporo prostych endpointów: zapis formularza, aktualizacja danych, wysyłka maila, zapis leadu.
Jak Server Actions zmieniają strony marketingowe?
Typowa strona marketingowa ma kilka formularzy: newsletter, kontakt, lead magnet, webinar, kalkulator z zapisem wyniku. W tradycyjnym podejściu każdy z nich łatwo kończył jako osobny endpoint API, walidacja po dwóch stronach i dodatkowy stan ładowania.
Z Server Actions ten sam formularz kontaktowy wygląda tak:
To fragment pokazujący granicę walidacji, a nie kompletny system antyspamowy.
Treść zgody musi odpowiadać rzeczywistemu celowi i podstawie przetwarzania.
source jest niezaufaną informacją analityczną, więc nie może sterować ceną,
uprawnieniem ani routingiem leada. saveLead powinien używać unikalnego tokenu
próby i indeksu w bazie, aby ponowienie po timeoutcie nie stworzyło drugiego
rekordu. Powiadomienie e-mail lepiej wysłać przez kolejkę lub wzorzec outbox po
udanym zapisie.
Co dostajesz:
- Formularz działa jako natywny mechanizm dzięki progressive enhancement w Server Component.
- Przepływ wymaga mniejszej liczby warstw, gdy mutację wywołuje wyłącznie interfejs React.
- Pomiar serwerowy nadal wymaga zgody, prawidłowej deduplikacji i zgodności z regułami platformy analitycznej.
- Logika zapisu pozostaje poza klientem, więc sekrety i połączenie z bazą nie trafiają do bundle'a.
Server Action przyjmuje bezpośrednie żądania POST z pominięciem widocznego formularza. Bezpieczne identyfikatory akcji, eliminacja martwego kodu i kontrola zgodności nagłówków Origin oraz Host tworzą dodatkową osłonę Next.js, lecz nie zastępują walidacji i autoryzacji w funkcji. Dla danych użytkownika sprawdź sesję i prawo do konkretnego rekordu. Publiczny formularz potrzebuje limitowania, ochrony antyspamowej oraz idempotencji. Wzorce kontroli dostępu opisuję w artykule o RBAC w Next.js.
Jeśli reverse proxy zmienia host widoczny dla aplikacji, skonfiguruj
serverActions.allowedOrigins jako krótką listę zaufanych domen. Nie dodawaj
szerokiego wildcardu tylko po to, aby uciszyć błąd Origin, bo rozszerza on
powierzchnię żądań akceptowanych przez aplikację.
Server Actions projektuj wyłącznie dla mutacji wywoływanych przez aplikację
React. useActionState kolejkuje powiązane wywołania, bo następna akcja otrzymuje
poprzedni stan. Dane odczytuj w Server Components, gdzie możesz jawnie ustalić
równoległość i cache. Webhook, endpoint dla aplikacji mobilnej oraz publiczne API
pozostają Route Handlerem albo osobną usługą.
Progressive Enhancement: formularz działa bez JavaScriptu
Server Actions w Next.js działają z natywnym HTML <form action>, więc formularz może działać nawet bez JavaScriptu klienta:
Użytkownik na wolnym łączu mobilnym wysyła formularz, zanim załaduje się JavaScript. Każda sekunda opóźnienia formularza to utracone leady.
działa
najprościej, gdy formularz w Server Component otrzymuje bezpośrednio Server
Action. Client Component kolejkuje wysłanie do zakończenia hydratacji, a
useActionState może użyć permalinka do nawigacji przed załadowaniem kodu.
Własny onSubmit z preventDefault odbiera przeglądarce natywną ścieżkę i
uzależnia formularz od JavaScriptu.
RSC nie naprawiają waterfalli: pobieraj dane równolegle i świadomie je cache'uj
Server Component eliminuje klientowy useEffect, ale nie skraca automatycznie czasu odpowiedzi serwera. Jeśli jedna funkcja czeka na drugą, użytkownik nadal czeka na sumę obu opóźnień. Niezależne dane uruchamiaj równolegle, a zależne pobieraj dopiero po otrzymaniu potrzebnego identyfikatora:
Druga decyzja to cache. Dane publiczne, które zmieniają się rzadko, warto cache'ować i unieważniać po mutacji. Dane zależne od sesji, koszyka czy uprawnień muszą pozostać per żądanie. W Next.js 16 Cache Components trzeba włączyć przez cacheComponents: true; dyrektywa 'use cache' może objąć funkcję pobierającą dane albo cały komponent. Nie dodawaj cache'u na ślepo: błędny klucz lub zbyt długie życie wpisu potrafi pokazać cenę, dostępność albo dane użytkownika w nieaktualnej wersji.
W praktyce optymalizację oceniaj w tej kolejności: najpierw usuń sekwencyjne oczekiwanie, potem wybierz cache dla danych publicznych, a wolne fragmenty odizoluj przez <Suspense>. RSC są fundamentem tego układu, nie zamiennikiem dla dobrej strategii danych.
Streaming i Suspense w Next.js: szybszy feedback dla użytkownika
Next.js z RSC wspiera streaming HTML. Serwer wysyła gotowe fragmenty strony w miarę ich renderowania, zamiast czekać aż cała strona będzie gotowa. W połączeniu z React Suspense pozwala to na natychmiastowe wyświetlenie szkieletu strony z progresywnym dociąganiem treści:
Streaming pozwala wcześniej wysłać gotową część dokumentu, ale nie gwarantuje
niskiego LCP. ProductHero w przykładzie może zablokować początek odpowiedzi,
jeśli sam długo pobiera dane. Element LCP umieszczony w późno rozwiązywanym
Suspense pojawi się dopiero wraz z odpowiednim fragmentem strumienia. Mierz TTFB,
czas dotarcia elementu LCP i kompletność odpowiedzi po zakończeniu streamingu.
Bot, który odczytuje pełną odpowiedź, może zobaczyć fragmenty wysłane później.
Next.js w przypadku botów wymagających metadanych w <head> wyłącza streaming
metadanych i czeka na generateMetadata. Listę takich botów można zmienić przez
htmlLimitedBots, ale nie konfiguruj jej bez testu wpływu na czas odpowiedzi.
Wariantem tego mechanizmu jest plik loading.tsx, czyli granica Suspense na
poziomie segmentu trasy. Przy nawigacji Next.js może wcześniej pokazać stan
ładowania, a właściwa strona dociera strumieniem. Ręczny <Suspense> daje
precyzyjniejszą kontrolę nad sekcjami. Fallback powinien rezerwować docelowe
wymiary. Szkielet o innej wysokości niż treść wygeneruje przesunięcie układu i
pogorszy CLS.
Partial Prerendering w Next.js: hybryda statycznego i dynamicznego renderowania
Partial Prerendering (PPR) łączy statyczny HTML z dynamicznymi komponentami w jednym żądaniu HTTP. Statyczna powłoka strony, na przykład nawigacja, stopka i układ, może zostać wysłana wcześniej. Personalizacja oraz bieżące dane docierają przez streaming.
To bardzo naturalny kierunek dla stron marketingowych:
- Katalog łączy powłokę z aktualną dostępnością oraz ceną pobieraną dynamicznie.
- Landing zachowuje statyczną główną treść, a personalizację umieszcza w osobnym fragmencie.
- Artykuł pozostaje w statycznej powłoce, natomiast komentarze docierają przez streaming.
Server Component czy Client Component: strategia wyboru
Najpierw zdecyduj, co zostaje Server Componentem (domyślne), a co trafia do Client Componentu (oznaczonego 'use client'). Zasada ogólna:
Server Component stosuj domyślnie do:
Treści statycznych (nagłówki, akapity, listy, karty artykułów)
- Pobierania danych (fetch z bazy, CMS, API)
- Renderowania markdown/MDX
- Komponentów, które nie wymagają interaktywności
SEO-critical content (meta tagi, structured data, treść indeksowalna)
Client Component wydzielaj wyłącznie do:
Interaktywnych formularzy z walidacją podczas wpisywania
Komponentów ze stanem, takich jak karty, akordeony, modale i listy rozwijane
Komponentów korzystających z Browser API (localStorage, geolocation, clipboard)
- Animacji reagujących na interakcje użytkownika
Zewnętrznych widgetów, takich jak mapy, czaty i osadzenia
Interakcja wyznacza granicę kodu klienta. Jeśli komponent nie potrzebuje
stanu, efektu, własnego hooka ani API przeglądarki, pozostaw go po stronie
serwera. Sama animacja CSS, link lub natywny formularz nie wymagają dyrektywy
'use client'.
Granica server/client w praktyce: kompozycja i serializacja
Dyrektywa 'use client' działa na zasadzie propagacji, czyli każdy komponent importowany przez komponent kliencki automatycznie staje się częścią bundle'a przesyłanego do przeglądarki. Nawet jeśli importujesz wyłącznie statyczną treść, całość poddrzewa importów traci status serwerowy. Nierozważne umieszczenie tej dyrektywy w layoutcie może przez to nieumyślnie przekształcić architekturę server-first w klasyczne SPA.
Rozwiązaniem jest kompozycja. Zamiast importować treść serwerową bezpośrednio do komponentu klienckiego, przekazuj ją jako children:
Interaktywne moduły trzymaj przy liściach drzewa, czyli w małych obszarach
stanu, do których serwer przekazuje gotową treść. 'use client' wyznacza granicę
grafu modułów. Komponent serwerowy przekazany jako children nie staje się z
tego powodu modułem klienta, ponieważ rodzic serwerowy wyrenderował go wcześniej.
Propsy muszą pozostać serializowalne przez React. Nie przekazuj do Client
Componentu całego rekordu użytkownika tylko dlatego, że mechanizm serializacji
go zaakceptuje. Utwórz obiekt DTO zawierający wyłącznie pola potrzebne w
interfejsie. Moduły z bazą, tokenami i logiką uprawnień oznacz przez
server-only. React Taint API może stanowić dodatkową barierę przed przypadkowym
przekazaniem wartości, lecz podstawą nadal jest świadoma selekcja danych.
Funkcji i instancji klas nie przekazuj przez zwykłe propsy. Server Actions są obsługiwanym wyjątkiem, bo React serializuje referencję potrzebną do wywołania funkcji na serwerze. Wpływ granicy na wagę kodu sprawdzisz analizą bundle'a, opisaną w artykule o analizie paczki JS w Next.js.
Wpływ React Server Components i Server Actions na SEO
Server-first daje robotowi dobry punkt wejścia, jeśli publiczna treść rzeczywiście znajduje się w odpowiedzi HTML. Googlebot i tak kieruje strony z kodem 200 do kolejki renderowania, nawet gdy odpowiedź zawiera gotową treść. RSC ograniczają zależność od wykonania klientowego fetcha, ale nie omijają procesu renderowania Google ani nie stanowią osobnego sygnału rankingowego.
Core Web Vitals
RSC mogą ograniczyć pracę głównego wątku, ponieważ kod Server Components nie wchodzi do paczki klienta. Ten zysk poprawia warunki dla INP. LCP zależy również od TTFB, priorytetu obrazu i położenia granic Suspense, a CLS od zarezerwowania miejsca. Porównuj dane terenowe przed migracją i po niej, zamiast opierać ocenę na samym wyniku Lighthouse z jednej sesji.
Odczyt treści i linków
Google wykonuje JavaScript, lecz część robotów społecznościowych, wyszukiwarek i
narzędzi AI odczytuje przede wszystkim odpowiedź HTTP. Publiczny tekst, linki z
href, dane strukturalne i główne metadane powinny być dostępne bez interakcji.
Nie ukrywaj podstawowej treści za kliknięciem obsługiwanym wyłącznie w Client
Component ani za klientowym zapytaniem uruchamianym po montażu.
Metadane i kody odpowiedzi
Metadata API działa w Server Components i pozwala generować tytuł, opis,
canonical oraz Open Graph z tego samego rekordu co strona. Dla dynamicznych tras
Next.js może streamować metadane, ale boty wymagające znaczników w <head>
otrzymują wariant blokujący. Nadal musisz wywołać notFound() dla brakującego
rekordu, ustawić przekierowanie po zmianie adresu i nie zwracać pustej strony z
kodem 200.
Audyt SEO po migracji na RSC
Pobierz URL przez
curli sprawdź status, canonical, robots, nagłówek H1, główną treść oraz linki bez uruchamiania JavaScriptu.Porównaj zwykłą odpowiedź z wyrenderowanym HTML-em w narzędziu do inspekcji URL. Różnica nie powinna zmieniać sensu strony ani linkowania.
Sprawdź JSON-LD po serializacji i nie przekazuj do skryptu surowego tekstu, który może zamknąć znacznik przez sekwencję HTML.
Zmierz LCP, INP i CLS w danych terenowych na poziomie typu strony oraz urządzenia. Wynik laboratoryjny służy do diagnozy, a nie kończy audytu.
Zweryfikuj 404, 301, błędy backendu i timeouty. Render serwerowy może szybciej dostarczyć również błędną odpowiedź, jeśli obsługa statusów kuleje.
Migracja z Pages Router na App Router przy server-first Next.js
Jeśli masz istniejący projekt Next.js na Pages Router, migracja na App Router (i tym samym na RSC) jest procesem przyrostowym:
- Zacznij od nowych stron. Nowe sekcje buduj w App Router (
app/directory), istniejące zostaw w Pages Router (pages/) - Najpierw migruj proste trasy treściowe. Blog, strona o firmie i dokumenty bez interakcji łatwo podzielić na Server Components.
- Wydziel małe Client Components. Przenieś do nich stan, efekty i obsługę zdarzeń, pozostawiając treść w rodzicu serwerowym.
- Przenieś mutacje interfejsu do Server Actions. Formularze kontaktowe i zmiany konta pasują do akcji, jeśli nie potrzebują publicznego kontraktu API.
- Przenieś pobieranie danych na serwer. Zastąp
getStaticPropsigetServerSidePropsasynchronicznymi komponentami oraz jawną strategią cache.
Next.js wspiera oba routery jednocześnie, więc migracja może być rozłożona w czasie bez ryzyka regresji.
Po każdym etapie porównaj HTML, paczkę JavaScript, statusy, metadane i dane terenowe. Oba routery mogą działać w jednym projekcie, lecz granica nawigacji między nimi może powodować pełne przeładowanie strony. Sprawdź też współdzielone style, analitykę i stan sesji.
Jeśli rozwijasz ten temat dalej, zobacz App Router czy Pages Router, co wybrać? i React 19 Actions.
