Skip to main content
Практика завдань

Як працюють серверні компоненти (rsc) у Next.js

React Server Components (RSC) — це компоненти, які виконуються виключно на сервері. Їхній код ніколи не потрапляє в JavaScript-бандл, який завантажує браузер. У Next.js App Router (починаючи з версії 13) усі компоненти за замовчуванням є серверними компонентами.

Чому існують серверні компоненти

До RSC весь код React відправлявся в браузер. Навіть якщо компонент просто відображав дані з бази даних, його код, залежності та логіка включалися в клієнтський бандл.

Серверні компоненти вирішують три проблеми:

  1. Розмір бандлу. Код серверних компонентів і їхні залежності не відправляються на клієнт. Якщо ви використовуєте важку бібліотеку для обробки даних, вона залишається на сервері.
  2. Прямий доступ до даних. Серверні компоненти можуть безпосередньо отримувати доступ до бази даних, файлової системи, змінних середовища. Не потрібен проміжний API-інтерфейс.
  3. Безпека. Секрети (API-ключі, рядки підключення до бази даних) ніколи не потрапляють до клієнта.

Серверні vs Клієнтські компоненти

tsx
// Серверний компонент (за замовчуванням) // Виконується на сервері, код не включений у бандл import { db } from '@/lib/db' export default async function ProblemsCount() { const count = await db.problem.count() return <p>Проблеми на IT Lead: {count}</p> }
tsx
// Клієнтський компонент // Позначений директивою 'use client' 'use client' import { useState } from 'react' export default function LikeButton() { const [liked, setLiked] = useState(false) return ( <button onClick={() => setLiked(!liked)}> {liked ? 'Не подобається' : 'Подобається'} </button> ) }

Коли використовувати що

ЗавданняСерверКлієнт
Отримання даних з БД/API
Доступ до змінних середовища, файлів
Важкі залежності (парсер markdown тощо)
useState, useEffect, useRef
onClick, onChange, onSubmit
API браузера (localStorage, геолокація)
React Context (useContext)

Як це працює під капотом

Рендеринг на сервері

Next.js виконує серверні компоненти та створює спеціальний формат (RSC Payload), який описує результат рендерингу.

Стрімінг до клієнта

RSC Payload відправляється в браузер разом з HTML. Клієнтські компоненти гідратуються, серверні компоненти вже відрендерені.

Оновлення UI

При навігації Next.js запитує RSC Payload для нового маршруту. Серверні компоненти повторно рендеряться на сервері, клієнтські компоненти зберігають свій стан.

Шаблони використання

Серверний компонент обгортає клієнтський компонент

Найбільш поширений шаблон: серверний компонент завантажує дані та передає їх клієнтському компоненту через props:

tsx
// app/problems/page.tsx (сервер) import { db } from '@/lib/db' import { ProblemList } from './problem-list' export default async function ProblemsPage() { const problems = await db.problem.findMany({ orderBy: { difficulty: 'asc' } }) return <ProblemList problems={problems} /> }
tsx
// app/problems/problem-list.tsx (клієнт) 'use client' import { useState } from 'react' export function ProblemList({ problems }) { const [filter, setFilter] = useState('all') const filtered = filter === 'all' ? problems : problems.filter(p => p.difficulty === Number(filter)) return ( <div> <select onChange={e => setFilter(e.target.value)}> <option value="all">Усі</option> <option value="1">Легкі</option> <option value="2">Середні</option> <option value="3">Важкі</option> </select> <ul> {filtered.map(p => ( <li key={p.id}>{p.name}</li> ))} </ul> </div> ) }

Серверний компонент як діти

Клієнтський компонент може отримувати серверний компонент через children. Це дозволяє створювати інтерактивні обгортки, не втрачаючи переваг серверного рендерингу:

tsx
// Клієнтський обгортковий компонент 'use client' import { useState } from 'react' export function Accordion({ title, children }) { const [open, setOpen] = useState(false) return ( <div> <button onClick={() => setOpen(!open)}>{title}</button> {open && children} </div> ) }
tsx
// Серверний компонент використовує клієнтську обгортку import { Accordion } from './accordion' import { db } from '@/lib/db' export default async function FAQ() { const items = await db.faq.findMany() return ( <div> {items.map(item => ( <Accordion key={item.id} title={item.question}> <p>{item.answer}</p> </Accordion> ))} </div> ) }

Важливо:

Ви не можете імпортувати серверний компонент всередині клієнтського компонента. Якщо ви додасте 'use client' до файлу, всі його імпорти також стануть клієнтськими. Передавайте серверні компоненти лише через props або children.

Обмеження серверних компонентів

  • Не можна використовувати хуки (useState, useEffect, useContext тощо)
  • Не можна додавати обробники подій (onClick, onChange)
  • Не можна використовувати API браузера
  • Не можна імпортувати безпосередньо в клієнтські компоненти

Порада для співбесіди:

Звичайна помилка на співбесіді: плутати серверні компоненти з SSR. SSR рендерить все дерево React на сервері один раз і відправляє HTML. Серверні компоненти — це інша модель: частина дерева постійно живе на сервері і ніколи не відправляє свій код на клієнт. При навігації серверні компоненти повторно рендеряться на сервері.

Корисні ресурси

  • nextjs.org/docs — документація по серверним компонентам
  • react.dev — документація по RSC в React
  • Шаблони композиції — шаблони для комбінування серверних та клієнтських компонентів

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань