Přeskočit na obsah
React & Next.js

React Server Components — kompletní průvodce

10 min čtení
ReactServer ComponentsNext.jsRSC

Kompletní průvodce React Server Components (RSC). Naučte se rozdíl mezi server a client komponentami, streaming, suspense a praktické vzory pro Next.js aplikace.

Co jsou React Server Components?

React Server Components (RSC) představují fundamentální změnu v tom, jak přemýšlíme o renderování React aplikací. Na rozdíl od tradičního client-side renderování nebo server-side renderování (SSR) přinášejí RSC třetí paradigma — komponenty, které se renderují výhradně na serveru a nikdy se nepřenášejí do prohlížeče jako JavaScript.

Hlavní motivací pro vznik RSC bylo snížení množství JavaScriptu posílaného klientovi. V tradičních React aplikacích se celý komponentový strom včetně závislostí stahuje do prohlížeče, i když mnoho komponent jen zobrazuje statická data. RSC tento problém řeší elegantně — serverové komponenty se renderují na serveru, a klientovi se posílá pouze výsledný HTML s minimálním JavaScriptem pro interaktivitu.

Koncept RSC byl poprvé představen týmem React v prosinci 2020, ale praktickou adopci umožnil až framework Next.js 13 s App Routerem, který RSC integroval jako výchozí chování. Dnes jsou RSC klíčovou součástí moderního React ekosystému a ovlivňují způsob, jakým architekti navrhují webové aplikace.

Architektura RSC

RSC zavádějí dvě kategorie komponent: Server Components (výchozí v Next.js App Routeru) a Client Components (označené direktivou 'use client'). Server Components mají přímý přístup k databázím, souborovému systému a dalším serverovým zdrojům. Client Components naopak mají přístup k browser API, useState, useEffect a dalším hookům vyžadujícím interaktivitu.

// Server Component (výchozí v Next.js App Router)
// Tento kód se NIKDY nepošle do prohlížeče
import { db } from '@/lib/database'

async function ProductList() {
  // Přímý přístup k databázi — žádné API volání
  const products = await db.query('SELECT * FROM products WHERE active = true')
  
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}

// Client Component — potřebuje interaktivitu
'use client'
import { useState } from 'react'

function AddToCartButton({ productId }) {
  const [loading, setLoading] = useState(false)
  
  const handleClick = async () => {
    setLoading(true)
    await fetch('/api/cart', {
      method: 'POST',
      body: JSON.stringify({ productId })
    })
    setLoading(false)
  }
  
  return (
    <button onClick={handleClick} disabled={loading}>
      {loading ? 'Přidávám...' : 'Přidat do košíku'}
    </button>
  )
}

Streaming a Suspense

Jednou z nejsilnějších vlastností RSC je podpora streamování obsahu. Místo čekání na kompletní vykreslení celé stránky může server posílat části UI postupně, jak se data načítají. Tento přístup výrazně zlepšuje vnímanou rychlost aplikace, protože uživatel vidí obsah okamžitě, zatímco pomalejší části se načítají na pozadí.

React Suspense slouží jako mechanismus pro deklarativní definici loading stavů. Když serverová komponenta provádí asynchronní operaci (například dotaz do databáze), Suspense boundary zobrazí fallback obsah, dokud se data nenačtou. Na klientovi se pak plynule nahradí skutečným obsahem bez jakéhokoli blikání.

import { Suspense } from 'react'

// Stránka s postupným načítáním
export default function DashboardPage() {
  return (
    <div className="dashboard">
      {/* Toto se zobrazí okamžitě */}
      <h1>Dashboard</h1>
      <NavigationBar />
      
      {/* Statistiky se načtou jako první */}
      <Suspense fallback={<StatsSkeleton />}>
        <DashboardStats />
      </Suspense>
      
      {/* Graf se načte nezávisle */}
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />
      </Suspense>
      
      {/* Tabulka s daty se může načíst jako poslední */}
      <Suspense fallback={<TableSkeleton />}>
        <RecentOrders />
      </Suspense>
    </div>
  )
}

// Každá komponenta si načte data nezávisle
async function DashboardStats() {
  const stats = await getStats() // pomalý dotaz
  return <StatsGrid data={stats} />
}

Parallel Data Fetching

RSC přirozeně podporují paralelní načítání dat. Každá serverová komponenta může nezávisle načítat svá data, a díky Suspense boundaries se tyto požadavky provádějí paralelně. To eliminuje klasický problém vodopádových požadavků (waterfall requests), kde se data načítají sekvenčně.

V praxi to znamená, že pokud máte stránku s profilem uživatele, jeho objednávkami a doporučeními, všechny tři datové zdroje se dotazují současně, místo aby čekaly jeden na druhého. Výsledek je dramatické zrychlení načítání stránek, zejména u aplikací s mnoha nezávislými datovými zdroji.

Composition Patterns

Správná kompozice serverových a klientských komponent je klíčová pro efektivní využití RSC. Základní pravidlo říká: server komponenty mohou importovat client komponenty, ale ne naopak. Client komponenty nemohou přímo importovat server komponenty, ale mohou je přijímat jako children props.

// ✅ Správný vzor: Server komponenta předá data client komponentě
// layout.tsx (Server Component)
import { getUser } from '@/lib/auth'
import { InteractiveNav } from '@/components/InteractiveNav'

export default async function Layout({ children }) {
  const user = await getUser()
  
  return (
    <div>
      {/* Client komponenta přijímá serializovatelná data */}
      <InteractiveNav userName={user.name} role={user.role} />
      {/* Server komponenty jako children */}
      {children}
    </div>
  )
}

// ✅ Správný vzor: Client wrapper se server children
'use client'
function TabPanel({ children, tabs }) {
  const [activeTab, setActiveTab] = useState(0)
  
  return (
    <div>
      <div className="tabs">
        {tabs.map((tab, i) => (
          <button key={i} onClick={() => setActiveTab(i)}>
            {tab.label}
          </button>
        ))}
      </div>
      {/* children mohou být server komponenty! */}
      <div className="tab-content">
        {children[activeTab]}
      </div>
    </div>
  )
}

Caching a revalidace

Next.js poskytuje sofistikovaný caching systém pro RSC. Data načtená v serverových komponentách se automaticky cachují, a vývojáři mají kontrolu nad strategií revalidace. Existují tři hlavní přístupy: statické cachování (data se cachují na dobu neurčitou), časová revalidace (data se obnoví po uplynutí zadaného intervalu) a on-demand revalidace (data se obnoví programaticky po specifické události).

Správná cache strategie je kritická pro výkon RSC aplikací. Příliš agresivní cachování vede k zastaralým datům, zatímco nedostatečné cachování zbytečně zatěžuje databázi a zpomaluje odpovědi. V praxi se osvědčuje kombinace časové revalidace pro většinu dat s on-demand revalidací pro kritické aktualizace, například po uložení formuláře.

// Statické cachování — data se načtou jednou při buildu
async function StaticContent() {
  const data = await fetch('https://api.example.com/content', {
    cache: 'force-cache' // výchozí chování
  })
  return <Content data={data} />
}

// Časová revalidace — data se obnoví každých 60 sekund
async function DynamicPrices() {
  const prices = await fetch('https://api.example.com/prices', {
    next: { revalidate: 60 }
  })
  return <PriceList prices={prices} />
}

// On-demand revalidace — po server action
'use server'
import { revalidatePath, revalidateTag } from 'next/cache'

async function updateProduct(formData) {
  await db.products.update(formData)
  
  // Revalidace konkrétní stránky
  revalidatePath('/products/' + formData.get('id'))
  
  // Nebo revalidace podle tagu
  revalidateTag('products')
}

Server Actions

Server Actions jsou těsně provázány s RSC a umožňují volat serverové funkce přímo z klientského kódu bez nutnosti vytvářet API endpointy. Funkce označené direktivou 'use server' se automaticky transformují na HTTP POST endpointy, které React volá transparentně. To dramaticky zjednodušuje full-stack vývoj a eliminuje boilerplate kód pro API vrstvy.

Server Actions podporují progresivní vylepšení — formuláře s Server Actions fungují i bez JavaScriptu, protože se zpracovávají jako standardní HTML formuláře. Toto je obzvláště důležité pro přístupnost a SEO, protože zajišťuje, že kritická funkcionalita je dostupná všem uživatelům bez ohledu na stav jejich JavaScriptu.

Praktické vzory a best practices

Při práci s RSC v produkčních aplikacích se osvědčilo několik vzorů. Prvním je co nejvíce logiky na serveru — pokud komponenta nepotřebuje interaktivitu, měla by zůstat serverová. Druhým vzorem je granulární client boundaries — místo označení celé stránky jako 'use client' je lepší vytvořit malé interaktivní ostrůvky obklopené serverovými komponentami.

Třetím důležitým vzorem je správné zacházení s props. Data předávaná z server do client komponent musí být serializovatelná — tedy žádné funkce, Date objekty ani cyklické reference. Místo toho je vhodné předávat primitivní typy, pole a jednoduché objekty. Pro komplexní data lze použít JSON serializaci s custom transformací.

Čtvrtým vzorem je error boundaries pro izolaci chyb. Každá sekce stránky by měla mít vlastní error boundary, aby chyba v jedné části nezbořila celou stránku. RSC přirozeně podporují error handling na úrovni route segmentů prostřednictvím error.tsx souborů.

Výkon a metriky

RSC přinášejí měřitelné zlepšení klíčových webových metrik. Time to First Byte (TTFB) se zlepšuje díky streamování, protože server může začít odesílat obsah okamžitě. First Contentful Paint (FCP) se zrychluje, protože prohlížeč dostává HTML místo prázdné stránky čekající na JavaScript. Largest Contentful Paint (LCP) se optimalizuje díky paralelnímu načítání dat.

Nejvýraznější zlepšení je však v Total Blocking Time (TBT) a Time to Interactive (TTI), protože se dramaticky snižuje množství JavaScriptu, který musí prohlížeč parsovat a vykonat. V reálných aplikacích jsme pozorovali snížení velikosti JavaScript bundlu o 30-60 % po migraci na RSC.

Shrnutí

React Server Components představují paradigmatický posun ve vývoji webových aplikací. Kombinují výhody server-side renderování s flexibilitou React komponentového modelu. Klíčem k úspěšné adopci je pochopení hranic mezi server a client komponentami, správné použití Suspense pro streaming a promyšlená cache strategie. RSC nejsou jen optimalizace — jsou to nový způsob myšlení o architektuře webových aplikací, který přibližuje React full-stack vývoji.

CORE SYSTEMS tým

Enterprise architekti a software inženýři.