Strona główna / Artykuły / Komponenty serwerowe React: architektura stojąca za renderowaniem bez pliku bundle

Komponenty serwerowe React: architektura stojąca za renderowaniem bez pliku bundle

Zrozum uzasadnienie istnienia komponentów serwerowych w React, począwszy od rozmiaru plików i problemów z procesem budowania aplikacji, aż po granicę między serwerem a klientem oraz zawartość danych przekazywanych przez RSC.

3487 słów

Jeśli poświęciłeś czas na próby zrozumienia React Server Components i wyszedłeś jeszcze bardziej zdezorientowany niż na początku, to nie oznacza, że coś oczywistego umyka ci uwadze. To znak, że wyjaśnienia, które znalazłeś, same były częścią problemu. Przez kilka lat oficjalne opisy określały Server Components jako „komponenty renderowane na serwerze”, co brzmi podejrzanie podobnie do tego, co już robiły getServerSideProps lub klasyczne renderowanie na serwerze w Next.js. Następnie pojawiło się twierdzenie, że komponenty te „przesyłają zero JavaScript do klienta”, co brzmi bardziej jak trik związany z wydajnością niż nowy sposób strukturyzowania aplikacji. Potem pojawił się App Router i nagle każdy plik w projekcie Next.js stał się domyślnie Server Componentem, chyba że na początku pliku umieści się specjalną ścieżkę.

To zamieszanie nie było twoją winą. Zdarzyło się to, ponieważ naprawdę nowy paradygmat opisywano za pomocą słownictwa zapożyczonego z starszego. Niniejszy przewodnik ma na celu zamknięcie tej luki. Po ukończeniu lektury powinieneś zrozumieć nie tylko mechanizmy komponentów serwerowych, ale także uzasadnienie, dla którego stanowią one zupełnie inny sposób myślenia o aplikacjach React.

Problem, który faktycznie rozwiązuje RSC

Zanim przejdziemy do tego, jak funkcjonują komponenty serwerowe, przydatne jest zrozumienie, co skłoniło React do ich stworzenia. Problemem nigdy nie było to, że renderowanie po stronie serwera odbywało się zbyt wolno. Prawdziwym problemem jest to, że model komponentów React został opracowany w oparciu o założenie, że wszystko dzieje się w przeglądarce, nawet w przypadkach, gdy to założenie powodowało niepotrzebne obciążenia.

Pułapka wielkości pliku

In the classic React model, every component you author turns into JavaScript that gets shipped to the browser, no exceptions. It makes no difference whether that component just renders some static markdown, does a simple date calculation, or hits a database. As long as it lives somewhere in your component tree, its code lives in your bundle too.

That creates an unforgiving trade-off. Adding more components inflates your JavaScript payload. A heavier payload pushes out your Time to Interactive. In practice, you end up delivering code to browsers that never had any reason to execute it.

The Waterfall Problem

Zanim pojawiły się Server Components, każdy komponent potrzebujący danych musiał je pobrać po dotarciu do klienta. Typowym podejściem było wyświetlenie tymczasowej struktury, uruchomienie funkcji useEffect, oczekiwanie na odpowiedź, a dopiero potem wyświetlenie rzeczywistej treści. Jeśli ta treść zawierała zagnieżdżony komponent z własnymi potrzebami dotyczącymi danych, cykl ten powtarzał się: kolejna funkcja efektu, kolejne oczekiwanie, kolejne opóźnienie dodawane do poprzedniego. Ta reakcja łańcuchowa to tzw. kaskadowe pobieranie danych i wyjaśnia, dlaczego wiele aplikacji React wydaje się wolnych, mimo że ich API backendowe odpowiadają szybko.

Jednym z rozwiązań było przeniesienie pobierania danych na poziom trasy za pomocą narzędzi takich jak getServerSideProps lub loaderów tras, ale wiązało się to z kosztem: niszczyło to samodzielny charakter komponentów. Strona musiała nagle znać dane, które koncepcyjnie należały do jej potomków, a komponenty przestały być niezależnymi jednostkami.

Opłata za hydrację

Tradycyjne renderowanie po stronie serwera polega na generowaniu HTML na serwerze, wysyłaniu go, a następnie ponownym renderowaniu całej aplikacji w JavaScript na stronie klienta, aby stała się interaktywna. Oznacza to, że każdy komponent, włączając te, które nigdy nie będą potrzebować żadnego zachowania po stronie klienta, musi przejść proces hydracji. W praktyce płaci się opłatę za interaktywność nawet w przypadku treści całkowicie statycznych.

React Server Components rozwiązują wszystkie te trzy problemy na poziomie architektury, a nie jako dodatkowe ulepszenie wydajności.

Model mentalny: Server Components to nie „SSR 2.0”

Oto podstawowa idea, na której opiera się cały ten przewodnik: Server Components to nie sposób renderowania stron. To odrębna kategoria komponentów.

Klasyczny React rozpoznawał tylko jeden rodzaj komponentu – ten, który działał w przeglądarce. Mógł przechowywać stan, wykonywać efekty i reagować na zdarzenia. Cały jego cykl życia, od utworzenia do usunięcia, odbywał się po stronie klienta.

Składniki serwera React dodają drugą kategorię. Składnik serwera jest wykonywany na serwerze. Może swobodnie edytować plik systemowy, bezpośrednio wyszukiwać dane w bazie danych lub korzystać z biblioteki, która działa tylko w środowisku Node.js. Nie może jednak używać useState, useEffect ani przypinać obsługi zdarzeń, ponieważ po prostu nigdy nie jest uruchamiany w przeglądarce. Nie ma dla niego etapu hydratacji. Jego kod nawet nie jest przesyłany do klienta w formie JavaScriptu.

Korzystny sposób na zrozumienie tego: drzewo składników React jest teraz złożone. Niektóre gałęzie są generowane na serwerze, inne w przeglądarce. Gałęzie po stronie serwera są renderowane dokładnie raz, podczas żądania, a ich wynik jest przesyłany do klienta w postaci zserwowanych elementów React. Gałęzie po stronie klienta są renderowane w przeglądarce jak zwykle i zachowują wszystkie funkcje interaktywne, do których jesteś przyzwyczajony.

To jest inny mechanizm niż SSR. Renderowanie po stronie serwera polega na przekształceniu całej aplikacji na serwerze w format HTML, a następnie ponownym uruchomieniu jej w przeglądarce. RSC natomiast renderuje tylko określone komponenty na serwerze i w ogóle nie przesyła ich kodu do przeglądarki.

Co faktycznie jest wysyłane do klienta

Komponent serwerowy nie wytwarza pliku HTML podczas renderowania. Zamiast tego generuje zserializowany opis swojego wyniku, znany jako RSC Payload – strumień instrukcji przypominających JSON, które mówią Reactowi po stronie klienta, jak zbudować drzewo elementów, które ma wyświetlić.

Oto sekwencja zdarzeń zachodzących w tle, gdy żądany jest strona zawierająca komponenty serwerowe:

  1. React renderuje komponenty serwerowe na serwerze.
  • Dla każdego komponentu serwerowego wysyła faktycznie wyrenderowany wynik — utworzone elementy, a nie kod źródłowy, który je stworzył.
  • Dla każdego komponentu klienckiego wysyła natomiast odnośnik: marker wskazujący, gdzie ten komponent powinien zostać zamontowany, wraz z właściwościami, których potrzebuje.
  • Serwer przesyła cały ten ładunek do przeglądarki.
  • Warstwa wykonawcza React w przeglądarce analizuje ten ładunek i buduje powstałe drzewo.
  • Komponenty klienckie następnie ściągają swój kod i są „hydratowane”. Komponenty serwerowe, będąc już wyrenderowanymi, w ogóle nie wymagają kroku hydratacji.
  • Kluczowym wnioskiem jest to, że kod komponentów serwerowych nigdy nie trafia do pliku JavaScript wysyłanego do przeglądarek. Wyobraźmy sobie komponent MarkdownRenderer zbudowany na bazie biblioteki do analizy Markdown o rozmiarze 200 KB. Jeśli ten komponent jest komponentem serwerowym, cała ta biblioteka o rozmiarze 200 KB pozostaje na serwerze. Przeglądarka widzi jedynie strukturę wygenerowaną przez parser, a nigdy sam parser.

    To jest istota twierdzenia o zerowym rozmiarze pliku, i nie jest to zwykła technika optymalizacyjna. Stanowi ona rzeczywistą zmianę w tym, co faktycznie oznacza budowa aplikacji React.

    Granica między serwerem a klientem

    W App Routerze każdy plik jest traktowany jako komponent serwerowy, chyba że zostanie to inaczej określone. Nie jest to jedynie stylowa domyślność — to strukturalne założenie wbudowane w framework, odzwierciedlające ideę, że większość elementów interfejsu w ogóle nie musi działać w przeglądarce.

    Komponent kliencki natomiast to kod przeznaczony do wykonywania na komputerze użytkownika. Aby tak oznaczyć plik, umieszcza się na jego początku instrukcję "use client". Nie jest to zwykły tekst informacyjny — pełni rolę ścisłej granicy. Instrukuje on kompilator React, że wszystko zdefiniowane w tym pliku, wraz z elementami zaimportowanymi, musi zostać spakowane i dostarczone do przeglądarki.

    Zasada, która zapewnia spójność całego tego systemu, jest następująca: Komponenty serwerowe mogą swobodnie importować i renderować komponenty klienckie, ale odwrotność nie jest nigdy dozwolona — komponent kliencki nie może importować komponentu serwerowego.

    Na pierwszy rzut oka wydaje się to sprzeczne. Czy kod serwerowy nie powinien być użyteczny z każdego miejsca, włącznie z plikami klienckimi? W rzeczywistości nie, jeśli przyjrzeć się temu, jak faktycznie przepływają dane:

    • Najpierw wykonywany jest komponent serwerowy, który ma bezpośredni dostęp do bazy danych i zasobów backendu. Pobiera wszystkie potrzebne dane i tworzy układ strony. W tym układzie może renderować coś w rodzaju <UserProfile />, co wymaga interaktywności — dlatego ta część jest napisana jako komponent kliencki. Komponent serwerowy przekazuje pobrane dane użytkownika jako atrybuty do komponentu klienckiego.
  • Komponent klienta po prostu wykorzystuje te właściwości i renderuje części interaktywne. Nie ma możliwości powrotu i zaimportowania komponentu serwerowego, ponieważ zanim uruchomi się w przeglądarce, wykonywanie na stronie serwera już dawno się zakończyło. Nie ma już żadnego procesu serwerowego do wywołania.
  • Dlatego taka konfiguracja nie może funkcjonować:

    // ❌ Impossible: Client Component importing Server Component
    'use client';
    import { ServerDataFetcher } from './ServerDataFetcher'; // This breaks!
    export function ClientWidget() {
      return (
        <div>
          <ServerDataFetcher /> {/* Cannot render a server component here */}
        </div>
      );
    }
    

    Jednak ustrukturyzowanie rzeczy w inny sposób jest całkowicie dopuszczalne:

    // ✅ Correct: Server Component importing Client Component
    // This is a Server Component (no 'use client')
    import { ClientWidget } from './ClientWidget';
    async function ServerDataFetcher() {
      const data = await db.query('SELECT * FROM posts');
    
      return (
        <div>
          <h1>Latest Posts</h1>
          {data.map(post => (
            <ClientWidget key={post.id} post={post} />
          ))}
        </div>
      );
    }
    

    Wzorzec jest prosty: komponent serwerowy zajmuje się pobieraniem danych i renderowaniem struktury, a następnie przekazuje dane do komponentów interaktywnych znajdujących się na liściach drzewa. Te komponenty liściowe obsługują kliknięcia, wysyłanie formularzy oraz animacje. Wynikowa architektura to w zasadzie drzewo renderowane na serwerze, z elementami interaktywnymi rozmieszczonymi na jego brzegach.

    Komponenty asynchroniczne: rewolucja w pobieraniu danych

    Klasyczny React nigdy nie pozwalał na użycie funkcji komponentów asynchronicznych. Bezpośrednie używanie await wewnątrz ciała komponentu nie było możliwe — trzeba było ręcznie konfigurować useEffect i zarządzać stanem ładowania.

    Komponenty serwerowe eliminują tę ograniczenie: mogą być async. Wydaje się to niewielką modyfikacją składniową, ale znacznie zmienia podstawową architekturę.

    // ✅ Server Component: Direct data access, no useEffect
    async function BlogPostList() {
      // This runs on the server. No fetch call in the browser.
      const posts = await db.post.findMany({
        orderBy: { createdAt: 'desc' },
        take: 10
      });
    
      return (
        <ul>
          {posts.map(post => (
            <li key={post.id}>
              <h2>{post.title}</h2>
              <p>{post.excerpt}</p>
            </li>
          ))}
        </ul>
      );
    }
    

    Zwróć uwagę na to, czego tutaj brakuje. Nie ma useState do śledzenia flagi ładowania, nie ma useEffect uruchamiającego żądanie, nie ma ręcznie tworzonej ekranu tymczasowego. Komponent po prostu mapuje rekordy z bazy danych bezpośrednio na elementy React. Żądania są teraz wykonywane dla każdego komponentu osobno, a nie dla każdej trasy, i odbywają się wyłącznie po stronie serwera, nigdy w przeglądarce.

    Dzięki temu problem wodospadu znika — serwer może jednocześnie przetwarzać każdy asynchroniczny komponent, zanim w ogóle wyśle odpowiedź do klienta. Wywołania bazy danych są realizowane w tym samym centrum danych co twoja warstwa backend, więc opóźnienie mierzone jest w mikrosekundach, a nie w milisekundach, które są typowe przy transmisjach przez połączenie mobilne.

    Dyrektywa „use client”: kiedy i dlaczego

    Dodanie "use client" oznacza plik jako należący do środowiska działania przeglądarki. Jest to konieczne wtedy, gdy komponent ma do czynienia z elementami typowo klienckimi:

    • Stan lokalny za pomocą useState lub useReducer
    • Haki cyklu życia, takie jak useEffect lub useLayoutEffect
    • Obsługa zdarzeń, np. onClick lub onSubmit
  • Bezpośrednie API przeglądarki — localStorage, window, document
  • Haki powiązane z kontekstem przeglądarki, w tym określone zastosowania useRouter lub funkcje takie jak useMediaQuery
  • Częstym błędem jest umieszczanie tej dyrektywy zbyt wysoko w drzewie komponentów. Programiści często przekształcają całą stronę w komponent kliencki tylko dlatego, że jeden z wbudowanych przycisków wymaga interaktywności.

    // ❌ Bad: Making the whole page client-side for one interactive element
    'use client';
    import { useState } from 'react';
    import { HeroSection } from './HeroSection'; // Static, could be server
    import { LikeButton } from './LikeButton';   // Interactive, needs client
    export default function Page() {
      return (
        <div>
          <HeroSection />
          <LikeButton />
        </div>
      );
    }
    

    W tym fragmencie HeroSection to czysto statyczna marka, która mogłaby bez problemu pozostać renderowana na serwerze. Jednak ponieważ "use client" zostało zadeklarowane na poziomie rodzica, każde importowanie poniżej niego — włączając tę statyczną sekcję — zostaje spakowane i wysłane do przeglądarki mimo wszystko.

    Rozwiązanie: Przesuń „use client” niżej w drzewie

    Rozwiązaniem jest utrzymanie samej strony jako komponentu serwera, a elementu interaktywnego traktowanie jako węzła liściowego, który jest do niej importowany.

    // ✅ Good: Page is server, only LikeButton is client
    // page.tsx (Server Component by default)
    import { HeroSection } from './HeroSection';
    import { LikeButton } from './LikeButton';
    export default function Page() {
      return (
        <div>
          <HeroSection />
          <LikeButton postId="123" />
        </div>
      );
    }
    
    // LikeButton.tsx
    'use client';
    import { useState } from 'react';
    export function LikeButton({ postId }) {
      const [liked, setLiked] = useState(false);
    
      return (
        <button onClick={() => setLiked(!liked)}>
          {liked ? '❤️' : '🤍'}
        </button>
      );
    }
    

    W takiej strukturze ani HeroSection, ani Page nie są nigdy wysyłane do przeglądarki w formie JavaScriptu. Tylko LikeButton, wraz z wywołaniem useState, trafia do przeglądarki. To jest praktyczny mechanizm stojący za celem zerowej wielkości pliku: jak najwięcej drzewa komponentów pozostawia się na serwerze, a obszar klienta przeznacza się dla tych konkretnych węzłów, które rzeczywiście wymagają interaktywności.

    Mieszanie: wzorzec, który czyni RSC potężnym

    Technika, która nadaje RSC prawdziwą siłę, to mieszanie – umieszczanie komponentów serwerowych wewnątrz komponentów klienckich, przekazywanie danych pochodzących z serwera jako atrybutów oraz umożliwianie tym komponentom klienckim eksponowania slotów children, które mogą zawierać dodatkowe komponenty serwerowe.

    // Layout.tsx (Server Component)
    import { Sidebar } from './Sidebar';
    import { AnalyticsProvider } from './AnalyticsProvider';
    export default async function DashboardLayout({ children }) {
      // Fetch user data on the server
      const user = await getCurrentUser();
      const permissions = await getUserPermissions(user.id);
    
      return (
        <div className="dashboard">
          <Sidebar user={user} permissions={permissions} />
    
          {/* AnalyticsProvider is a Client Component */}
          <AnalyticsProvider userId={user.id}>
            {/* children here can be a Server Component page */}
            <main>{children}</main>
          </AnalyticsProvider>
        </div>
      );
    }
    
    // AnalyticsProvider.tsx
    'use client';
    import { createContext, useContext } from 'react';
    const AnalyticsContext = createContext(null);
    export function AnalyticsProvider({ userId, children }) {
      // Client-side analytics initialization
      useEffect(() => {
        analytics.identify(userId);
      }, [userId]);
    
      return (
        <AnalyticsContext.Provider value={{ userId }}>
          {children}
        </AnalyticsContext.Provider>
      );
    }
    

    Tutaj DashboardLayout działa na serwerze i bezpośrednio wysyła zapytania do bazy danych. Przekazuje te dane do Sidebar, który może sam w sobie być komponentem serwerowym lub klienckim w zależności od swoich potrzeb. Ponadto otacza resztę strony komponentem AnalyticsProvider, który jest komponentem klienckim niezbędnym w przeglądarce do uruchomienia biblioteki analitycznej od third party.

    Ważnym elementem jest właściwość children. To, co jest renderowane wewnątrz AnalyticsProvider, nie musi automatycznie stać się kodem klienckim tylko dlatego, że znajduje się tam. React przekazuje już zrenderowany wynik komponentu serwerowego do komponentu klienckiego poprzez children — komponent kliencki pełni tu jedynie rolę otoczki lub granicy, a nie konwertera, który zmusza wszystko w jego wnętrzu do działania po stronie klienckiej.

    Dokładnie w ten sposób budowane są hybrydowe interfejsy użytkownika: treść renderowana na serwerze jest umieszczana wewnątrz otoczek dostępnych tylko dla klienta, dokładnie tam, gdzie naprawdę potrzebna jest interaktywność.

    Ładunek RSC: spojrzenie pod maskę

    Renderowanie komponentu serwerowego nie generuje bezpośrednio HTML. Zamiast tego React wysyła Ładunek RSC, strumień binarny, który koncepcyjnie przypomina coś takiego:

    1:I["node_modules/react/jsx-runtime.js", "jsx"]
    2:I["./components/ClientWidget.js", "default"]
    0:["quot;, "div", null, {"children": [
      ["quot;, "h1", null, {"children": "Latest Posts"}],
      ["quot;, "@2", null, {"postId": "123", "title": "Hello World"}]
    ]}]
    

    Każdy wiersz w tym strumieniu to instrukcja. Symbol $ oznacza element Reacta. I wskazuje na import modułu komponentu klienckiego. Odniesienie typu @2 pokazuje drugi import – w tym przypadku ClientWidget. Należy zauważyć, że serwer już w pełni wyrenderował tag h1, natomiast w przypadku ClientWidget jedynie przekazał mu właściwości i wskazał miejsce, gdzie znajduje się jego kod, nie wyrenderowując go samodzielnie.

    Gdy przeglądarka otrzymuje ten strumień, rozwiązuje te odniesienia importowe, pobiera niezbędne pakiety komponentów klienckich i buduje ostateczną strukturę drzewa komponentów. Ponieważ dane przychodzą stopniowo, przeglądarka nie musi czekać na całość, zanim rozpocznie renderowanie. To stopniowe dostarczanie jest właśnie tym, co umożliwia RSC obsługę renderowania na serwerze w formie strumieniowej, omijając przy tym typowy problem związany z procesem hydratacji.

    Kasowanie i ponowna walidacja

    Next.js 15 i nowsze dają komponentom serwerowym dostęp do całego zakresu strategii kasowania:

    1. Renderowanie statyczne (standardowe). Chyba że komponent odczytuje dane dynamiczne lub wykonuje niezaszyfrowane żądanie, jest renderowany statycznie podczas budowania aplikacji.
    // Cached indefinitely at build time
    async function ProductList() {
      const products = await fetch('https://api.example.com/products');
      // ...
    }
    
    1. Renderyzacja dynamiczna. Wywoływanie funkcji cookies(), headers() lub odczytywanie wartości z searchParams, a także wyraźne ustawienie export const dynamic = 'force-dynamic', zmuszają komponent do renderowania się przy każdej nowej prośbie.
    2. Ponowna weryfikacja po upływie określonego czasu.
    // Revalidate every 60 seconds
    async function ProductList() {
      const products = await fetch('https://api.example.com/products', {
        next: { revalidate: 60 }
      });
      // ...
    }
    
    1. Ponowna weryfikacja uruchamiana na żądanie.
    // app/api/revalidate/route.ts
    import { revalidatePath } from 'next/cache';
    export async function POST() {
      revalidatePath('/products');
      return Response.json({ revalidated: true });
    }
    

    Głównym wnioskiem jest to, że zachowanie cache’u jest teraz powiązane z poszczególnymi komponentami, a nie z całymi trasami. Dwa komponenty znajdujące się na tej samej stronie mogą stosować zupełnie różne zasady cache’owania. Taka precyzja nie ma odpowiednika w klasycznym SSR, gdzie jeden nagłówek cache’u dotyczy całej strony jednocześnie.

    Trwałe nieporozumienia

    "Komponenty serwerowe istnieją głównie ze względu na SEO." To nie do końca prawda — lepsze indeksowanie wyszukiwarek jest efektem ubocznym, a nie celem. Prawdziwą motywacją jest zmniejszenie ilości JavaScriptu po stronie klienta oraz umożliwienie pobierania danych na poziomie komponentów.

    "Każdy komponent, który używa hooka, wymaga use client." Jest to prawdą tylko wtedy, gdy hook faktycznie polega na API przeglądarki. Hook use z React 19 może rozpakowywać obietnice i bezpośrednio czytać kontekst wewnątrz komponentów serwerowych, więc wiele hooków funkcjonuje bezproblemowo bez konieczności kontaktu z klientem.

    "Komponenty serwerowe sprawiają, że trasy API stają się przestarzałe." To nieprawda — oba te elementy działają razem. Komponenty serwerowe przejmują zadanie pobierania danych podczas pierwszej renderizacji, ale nadal potrzebne są trasy API do obsługi modyfikacji, webhooków od stron trzecich oraz wszelkich operacji pobierania danych po stronie klienta po procesie hydratacji.

    "Komponenty serwerowe nie mają stanu." Brakuje im stanu w stylu przeglądarki — nie ma dostępnego useState — ale mogą swobodnie korzystać z źródeł stanu po stronie serwera, takich jak bazy danych, pamięci cache, system plików oraz zmienne środowiskowe.

    "Context jest niedostępny w komponentach serwerowych." Rzeczywiście nie można używać React Context wewnątrz komponentu serwerowego, ponieważ Context istnieje właśnie po to, by uniknąć konieczności przekazywania wartości przez wiele poziomów w drzewach renderowanych na stronie klienta. Zamiast tego można przekazywać dane jako zwykłe właściwości, a ponieważ renderowanie na serwerze rozwiązuje drzewo komponentów synchronicznie od góry do dołu, Next.js oferuje również opcje takie jak unstable_rootParams oprócz standardowego przekazywania właściwości.

    Sytuacja na rok 2026

    Ponieważ React 19 osiągnął stabilność, narzędzia towarzyszące mu znacznie się rozwinęły:

    • Akcje serwera umożliwiają komponentom klienckim wywoływanie funkcji asynchronicznych, które działają bezpośrednio na serwerze, co złagodzi granicę pomiędzy klientem a serwerem w kontekście modyfikacji danych.
    • Hook use daje komponentom klienckim możliwość rozpakowywania obietnic i kontekstu bez konieczności korzystania z useEffect.
    • Częściowe wstępne renderowanie (PPR) w Next.js 15 umożliwia dostarczanie statycznej struktury bezpośrednio z CDN, podczas gdy dynamiczne sekcje są ładowane z serwera źródłowego.
    • React Compiler automatycznie zajmuje się memetyzacją komponentów klienckich, redukując konieczność ręcznego używania useMemo i sprawiając, że przejście między klientem a serwerem jest bardziej płynne.

    Model koncepcyjny osiągnął już stabilność. Komponenty serwerowe nie są już funkcją, którą można aktywować – stanowią one podstawowe założenie przy budowie nowoczesnych aplikacji React. Komponenty klienckie są teraz wyjątkiem: celowym rozwiązaniem przeznaczonym dla warstwy interaktywnej.

    Zmiana w architekturze, a nie tylko wydajności

    Komponenty serwerowe React to nie tylko drobna poprawka wydajności. Oznaczają one ponowne przemyślenie tego, gdzie faktycznie wykonywany jest kod React. Przez około dziesięć lat React był zasadniczo biblioteką do przeglądarek, którą dostosowano do działania po stronie serwera głównie ze względu na wymagania SEO. Dziś jest czymś zupełnie innym: pełnoprawnym systemem komponentów działającym po obu stronach połączenia sieciowego, przy czym każda ze stron ma dobrze określone granice, obowiązki i profile wydajnościowe.

    Serwer przestał być jedynie miejscem, z którego pobierane są dane — jest teraz prawdziwym środowiskiem renderowania. A przeglądarka nie jest już jedynym miejscem dla komponentów React; to właśnie tam znajduje się interaktywność.

    Gdy to ujęcie stanie się jasne, wiele niejasności związanych z RSC znika. Pytanie zmienia się z „Czy potrzebuję tutaj use client?” na „Gdzie powinna znajdować się ta konkretne część logiki?”. To jest pytanie, które warto zadać, i to właśnie taka mentalność pozwala rozwijać aplikacje w miarę ich wzrostu.

    Literatura pokrewna

  • Przejście na CSS-in-JS poza czasem ruchu: komponenty serwerowe i stylizacje w czasie budowy — Dlaczego CSS-in-JS w czasie ruchu koliduje z komponentami serwerowymi React i celami dotyczącymi wydajności, jak porównują się Tailwind, CSS Modules oraz biblioteki bez konieczności wykonywania kodu w czasie ruchu, oraz jak bezpiecznie przeprowadzić migrację.