React Server Components (RSC) вводят принципиально иную модель рендеринга, в которой компоненты могут выполняться либо на сервере, либо на клиенте, но при этом остаются частью единого дерева React.
Ключевая идея заключается в том, что:
Серверные компоненты не попадают в бандл JavaScript, отправляемый в браузер. Это означает:
В RSC модель разделяет компоненты на два типа.
По умолчанию все компоненты являются серверными, если не указано обратное.
Они:
Пример:
// Server Component
async function UserList() {
const users = await db.user.findMany();
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Здесь отсутствует клиентский JavaScript, а данные загружаются напрямую.
Клиентские компоненты явно обозначаются директивой:
"use client";
Они:
Пример:
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onCl ick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
Серверные компоненты могут импортировать клиентские, но не наоборот.
Это создаёт строгую иерархию:
Причина: серверный код не должен попадать в клиентский bundle.
В классической модели React данные передаются через API-запросы:
В RSC модель выглядит иначе:
Это уменьшает количество сетевых запросов и упрощает архитектуру.
Server Actions позволяют выполнять серверные функции напрямую из компонентов без явного API-слоя.
Они обозначаются директивой:
"use server";
"use server";
import db from "@/lib/db";
export async function createUser(formData) {
const name = formData.get("name");
await db.user.create({
data: { name }
});
}
import { createUser } from "./actions";
export default function UserForm() {
return (
<form action={createUser}>
<input name="name" placeholder="Name" />
<button type="submit">Create</button>
</form>
);
}
Форма отправляется напрямую на серверную функцию без ручного fetch.
До появления Server Actions стандартная схема выглядела так:
Теперь:
Разница:
Несмотря на удобство, существуют важные ограничения:
Обе технологии работают совместно:
Пример комбинированного подхода:
// server component
import { createPost } from "./actions";
export default async function Page() {
const posts = await db.post.findMany();
return (
<div>
<form action={createPost}>
<input name="title" />
<button type="submit">Add</button>
</form>
<ul>
{posts.map(p => (
<li key={p.id}>{p.title}</li>
))}
</ul>
</div>
);
}
RSC использует сериализацию дерева компонентов, а не JSON API.
Это означает:
Пример:
// Server Component
import ClientButton from "./ClientButton";
export default function Page() {
const time = new Date().toISOString();
return <ClientButton time={time} />;
}
"use client";
export default function ClientButton({ time }) {
return <button>{time}</button>;
}
Клиентские компоненты всё ещё требуют гидрации:
Но отличие RSC:
В традиционном React:
В RSC:
Это убирает необходимость:
UI строится вокруг серверных компонентов:
Клиентские компоненты используются как острова интерактивности:
Логика и UI располагаются рядом:
window.location // ошибка
Сервер не имеет доступа к браузеру.
"use client";
import db from "@/lib/db"; // ошибка
Client bundle не может содержать серверные модули.
Нельзя передавать:
RSC и Server Actions дают следующие оптимизации:
Особенно заметно в:
Система React с Server Components работает как конвейер:
На практике RSC и Server Actions тесно связаны с Next.js App Router:
Это делает архитектуру приложения более декларативной и централизованной.