Dla stron marketingowych, blogów, landing page'y i serwisów content-driven liczą się trzy rzeczy: szybkość, indeksowalny HTML i formularze, które działają bez nerwowego czekania na klientowy JavaScript.
Dlaczego server-first w Next.js stał się domyślnym podejściem?
Przez lata webowe aplikacje React przesuwały coraz więcej logiki na stronę klienta. Efektem były rosnące JavaScript, loading spinnery i strony, które bez JS były puste — niewidoczne dla botów wyszukiwarek i niedostępne na słabszych urządzeniach.
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)
Strona jest natychmiastowo gotowa do indeksowania, bez czekania na
To nie jest eksperyment do pobawienia się po godzinach. To normalny sposób pracy z App Routerem i dobry punkt startu dla nowych stron content-driven.
React Server Components: jak działają i jak wpływają na wydajność?
Jak React Server Components działają pod maską?
W tradycyjnym modelu React każdy komponent (nawet statyczna stopka czy lista benefitów) był częścią JavaScript bundle'a wysyłanego do przeglądarki. Przeglądarka musiała pobrać JS, zparsować go, wykonać i wyrenderować HTML (hydratacja).
W modelu RSC:
- Server Component renderuje się na serwerze do HTML i specjalnego formatu RSC Payload.
- Przeglądarka otrzymuje gotowy HTML (natychmiast widoczny) i payload opisujący strukturę.
- Client Components (oznaczone
'use client') dołączają interaktywność tylko tam, gdzie jest potrzebna. - Reszta strony nie wymaga ani bajta JavaScriptu, czyli chodzi tutaj o nagłówki, treść tekstową, nawigację i stopkę.
Wpływ React Server Components na Core Web Vitals
Core Web Vitals nie poprawiają się od samego użycia RSC, tylko wtedy, gdy RSC ograniczają ilość JavaScriptu, skracają czas do HTML-a i stabilizują layout.
Largest Contentful Paint (), czyli czas do wyrenderowania największego elementu widocznego na ekranie. RSC mogą pomóc, bo krytyczny HTML może być gotowy bez czekania na pobranie i wykonanie dużego bundle'a. Cel Google to poniżej 2,5 sekundy.
Interaction to Next Paint (). Opóźnienie między interakcją użytkownika a wizualną reakcją. Mniejszy bundle JavaScript oznacza mniej pracy parsowania i wykonania, co przekłada się na szybszą responsywność. Cel Google: poniżej 200 milisekund.
Cumulative Layout Shift (CLS). Niestabilność layoutu podczas ładowania, gdy układ strony jest znany od początku, łatwiej uniknąć przeskoków podczas hydratacji. Cel Google to poniżej 0,1.
Przykład React Server Components w sekcji blogowej
Rozważmy typową stronę blogową z listą artykułów i sidebarą z kategoriami. W tradycyjnym podejściu client-side:
Bot i użytkownik na start widzą „Ładowanie...” zamiast treści, a LCP cierpi, bo HTML jest pusty do momentu zakończenia fetcha. Cały kod, w tym useState, useEffect i logika pobierania danych, trafia do bundle'a klienta.
W podejściu server-first z RSC:
Zwróć uwagę na dwie rzeczy. Po pierwsze, nie ma tu useState na wartość pola — niekontrolowany input z atrybutem name w zupełności wystarcza, a useActionState (standard od React 19) dostarcza stan odpowiedzi i flagę pending bez ręcznej księgowości. Po drugie, akcja jest przekazana bezpośrednio do action formularza, więc React potrafi obsłużyć wysyłkę nawet przed hydratacją, a o tym przeczytasz więcej w sekcji o progressive enhancement. Więcej wzorców z tej rodziny 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:
Co dostajesz:
- Niezawodność. Formularz działa nawet gdy JavaScript klienta się nie załaduje (progressive enhancement).
- Prostszy przepływ. Mniej plików i mniej ręcznie sklejanej komunikacji klient-serwer.
- Server-side tracking. Zdarzenia konwersji wysyłane z serwera, niezależne od ad blockerów i ograniczeń przeglądarki.
- Mniejszy bundle. Logika walidacji i zapisu nie trafia do przeglądarki.
Pamiętaj o dwóch sprawach. Pierwszej, Server Action to publiczny endpoint HTTP i Next.js generuje dla niej adres, który można wywołać curl-em, z pominięciem formularza i całego UI. Walidacja przez Zod (jak wyżej) i sprawdzenie uprawnień przy akcjach za logowaniem jest obowiązekowa. Na marginesie, wzorce autoryzacji rozpisuję w artykule o RBAC w Next.js. Druga sprawa, Server Actions służą do mutacji, a nie do odczytu. Każde wywołanie to POST, a akcje wykonują się sekwencyjnie. Pobieranie danych przez akcję blokuje równoległość i omija cache. Dane czytasz w Server Components, akcjami je zmieniasz.
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 tylko wtedy, gdy do action trafia bezpośrednio Server Action (jak wyżej) albo akcja opakowana w useActionState (jak w formularzu newslettera). Wtedy React potrafi odtworzyć wysyłkę sprzed hydratacji. Gdy podłączysz własny kliencki handler (action={async (fd) => {...}}) albo onSubmit z preventDefault, formularz bez JavaScriptu jest martwy, kod wygląda niemal identycznie, a gwarancja znika po cichu.
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:
Z perspektywy SEO i UX:
- Użytkownik widzi treść natychmiast (główna sekcja produktu),
- LCP jest niski, bo najważniejszy element jest renderowany jako pierwszy,
- Sekcje drugorzędne (recenzje, polecane) dociągają się płynnie,
- Bot widzi kompletny HTML po zakończeniu streamingu,
Wariantem tego samego mechanizmu jest plik loading.tsx. To na poziomie całej trasy, więc Next.js pokazuje go natychmiast przy nawigacji, a strona streamuje się pod spodem. Pamiętaj, że loading.tsx jest dla całych podstron, a ręczny <Suspense> tam, gdzie chcesz wypuścić główną treść od razu i dociągać tylko sekcje drugorzędne. Fallback niech rezerwuje docelowe wymiary (skeleton, a nie spinner w pustym divie) inaczej streaming, który miał poprawić UX, zacznie generować przeskoki layoutu i psuć CLS. Dodam, że przez nieopatrzność, miałem taką sytuację na stronie StriveLab.
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 (nawigacja, footer, layout) jest serwowana od razu, a dynamiczne fragmenty (personalizowany content, dane real-time) doładowują się przez streaming.
To bardzo naturalny kierunek dla stron marketingowych:
- Strony katalogowe — statyczny layout + dynamiczne ceny i dostępność
- Landing page z personalizacją — statyczna struktura + dynamiczny headline/CTA na podstawie UTM
- Blog z komentarzami — statyczny artykuł + dynamiczna sekcja komentarzy
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 (domyślny) stosuj 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 ('use client') stosuj do:
- Interaktywnych formularzy z walidacją real-time
Stanowych komponentów (tabs, accordiony, modalne, dropdown)
Komponentów korzystających z Browser API (localStorage, geolocation, clipboard)
- Animacji reagujących na interakcje użytkownika
- Widgetów third-party (mapy, chaty, embedy)
Zasada kciuka: jeśli komponent nie potrzebuje useState, useEffect ani obsługi zdarzeń przeglądarki — powinien być Server Component.
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 komponenty projektuj jako liście drzewa, czyli wąskie wyspy stanu, do których serwer wlewa gotową treść. Druga zasada dotyczy propsów, w której wszystko, co przechodzi z serwera do klienta, musi być serializowalne (obiekty, tablice, stringi, liczby, daty). Funkcji, instancji klas ani klienta bazy nie przekażesz, choć jest wyjątek: Server Actions wolno przekazywać jako propsy, bo React zamienia je w bezpieczne referencje do endpointów. Ile ta dyscyplina realnie waży, sprawdzisz analizą bundle'a, a metodę opisuję w artykule o bundle analysis w Next.js.
Wpływ React Server Components i Server Actions na SEO
Server-first daje SEO lepszy punkt startu, bo crawler dostaje więcej gotowej treści w HTML i mniej zależy od wykonania JavaScriptu.
Core Web Vitals
LCP może się poprawić, bo krytyczny HTML jest dostępny wcześniej i nie czeka na duży bundle JavaScript
INP ma lepsze warunki, gdy wysyłasz mniej kodu do klienta i zostawiasz przeglądarce mniej pracy
CLS łatwiej utrzymać w ryzach, gdy layout jest kompletny i przewidywalny od początku
Crawlability
Treść może być dostępna w HTML źródłowym bez czekania na fetch po stronie klienta
Boty wyszukiwarek i AI crawlery dostają stabilniejszy HTML na start
Structured data (JSON-LD) może być generowane po stronie serwera z pełnymi danymi
Czas indeksacji
Strony z kompletnym HTML mają mniej zależności od drugiej fali renderowania JavaScriptu
Nowe treści mają większą szansę zostać poprawnie odczytane już z początkowego HTML-a
Mniej rozjazdów między HTML-em serwera a renderem klienta oznacza mniej ryzyka błędów indeksacji
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/) - Migruj strony statyczne najpierw. Blog, about, polityka prywatności — te strony najłatwiej konwertować na Server Components
- Wydziel Client Components. Zidentyfikuj interaktywne fragmenty i wydziel je do osobnych plików z
'use client' - Zastąp API routes Server Actions. Formularze kontaktowe, zapis na newsletter, proste mutacje
- Zmigruj data fetching.
getStaticProps/getServerSidePropszamień naasyncServer Components z bezpośrednimfetchlub dostępem do bazy
Next.js wspiera oba routery jednocześnie, więc migracja może być rozłożona w czasie bez ryzyka regresji.
Jeśli rozwijasz ten temat dalej, zobacz też App Router czy Pages Router — co wybrać? i React 19 Actions.
