Learning
Všechny články
Frontend28. září 2026 6 min

Server Components neposílají kód, posílají výsledek

Hranice mezi serverem a klientem je hranice serializace

Omezení RSC nevyplývají z toho, kde kód běží, ale z toho, co projde drátem

React Server Components se obvykle vysvětlují polohou: tyhle komponenty běží na serveru, tyhle v prohlížeči. To je pravda a je to k odvození pravidel málo.

Užitečnější formulace: mezi Server a Client Component leží serializační hranice. Server Component se vyrenderuje a dolů se pošle popis toho, co vyrenderovala. Ne její kód, ne její závislosti. Každé pravidlo, o které se člověk v praxi řízne, je důsledek téhle jediné věty.


Co je to vlastně za prostředí

Dokumentace to definuje přesně a je snadné to přečíst povrchně: Server Components se renderují „ahead of time, před bundlingem, v prostředí odděleném od tvojí klientské aplikace i od SSR serveru".

Jsou tam tři prostředí, ne dvě. RSC prostředí, SSR a prohlížeč. To RSC prostředí může běžet v build time na CI, pokud je obsah statický, nebo per request na serveru, pokud není. Že jde o tu samou věc spuštěnou v jiném okamžiku, je důvod, proč se v RSC nedá sáhnout na request, pokud to framework výslovně nepovolí.

Z toho, že to prostředí předchází bundleru, plyne ta nejhmatatelnější výhoda. Když Server Component použije marked a sanitize-html, aby z markdownu udělala HTML, prohlížeč nedostane ani tyhle knihovny, ani tu komponentu. Dostane hotový výsledek. Závislost použitá výhradně na serveru z bundlu nezmizí optimalizací, ona do něj nikdy nevstoupí.

Server Components taky smí být async a await psát přímo v renderu. To je věc, kterou klientský render neumí, protože ten musí být synchronní a znovuspustitelný.


Serializovatelnost je to skutečné pravidlo

Dolů jde popis výsledku, takže projde jen to, co se dá zapsat. Primitiva, objekty a pole z nich, vyrenderované JSX, a promisy. Neprojde funkce, closure, symbol ani instance třídy — protože ty nejsou daty, jsou odkazem na kód a prostředí, ve kterém vznikly.

Tohle je ta hrana, o kterou se člověk skutečně řízne. Callback nemůžeš předat Client Componentě jako prop ze Server Componenty, protože ho není jak přenést. Nejde o bezpečnostní pravidlo ani o svévoli, jde o to, že closure nemá serializovaný tvar.

Promise ale projde. Server Component smí dotaz na data odstartovat a nedokončený promise poslat dolů, kde ho Client Component přečte přes use() uvnitř Suspense boundary. Dostaneš tím dvě věci naráz: dotaz odjede na serveru v momentě, kdy se o něm ví, a klient na něj čeká až tam, kde se jeho výsledek zobrazuje. Tím se ruší waterfall, aniž bys data tahal nahoru do rodiče.


Směrnice značí vstupní bod, ne interaktivitu

"use client" se čte jako „tahle komponenta je interaktivní". Přesnější je: tohle je vstupní bod do klientského bundlu. Označuje hranici, od které kód jde do prohlížeče, a Server Component pak takovou komponentu skládá tím, že jí posílá vyrenderovaný obsah jako props nebo children.

A na tomhle místě je nejčastější omyl v celé oblasti. Dokumentace ho pojmenovává výslovně: pro Server Components žádná směrnice neexistuje. "use server" neznačí Server Component, značí Server Function. Server Component je default — je jí všechno, co není za "use client".

Praktické důsledky dvou z těch faktů: Context vytvořený na serveru dolů neprojde, protože jeho hodnota je běžný objekt vázaný na provider v jiném prostředí. A useState v Server Componentě není zakázaný z opatrnosti, ale protože není komu ten stav držet — ten render už proběhl a skončil.


Co si z toho odnést

Až budeš příště řešit, proč něco přes hranici nejde, neptej se, kde to běží. Zkus to zapsat jako data. Když to zapsat neumíš, neprojde to, a odpověď je předat místo toho výsledek nebo Server Function.

V hlavě se vyplatí držet dvě věty. Server Component je default a "use client" je vstupní bod do bundlu, ne příznak interaktivity. A hranice mezi nimi propouští hotový výsledek plus serializovatelná data, což je zároveň důvod, proč z bundlu vypadnou serverové závislosti, a důvod, proč tam nepředáš callback.

Promise přes hranici projde. To je nejvíc nedoceněná část RSC — dovoluje ti spustit dotaz tam, kde se o něm ví, a čekat na něj tam, kde se zobrazuje.

Zdroje

Poslech místo čtení — 2 zdroje k načtení.