Пошук уроків, статей та іншого контенту
Порівняєте Server Components і Client Components та визначите, де виконувати логіку кожного типу.
React-компонент — це функція, яка описує частину інтерфейсу:
function Welcome({ name }) {
return <h1>Вітаємо, {name}!</h1>;
}Компонент може виконуватися:
на сервері — Server Component;
у браузері — Client Component.
Це не два різні синтаксиси React. Обидва типи використовують JSX і можуть повертати розмітку. Відмінність полягає в тому, де виконується код і які можливості він має.
Важливо: підтримка Server Components залежить від середовища React. Наприклад, у Next.js App Router компоненти за замовчуванням є Server Components.
Server Component виконується на сервері. Його результат передається клієнту, а сам код компонента не потрібно завантажувати в браузер.
Server Components зручно використовувати для:
отримання даних із сервера;
роботи з базою даних або серверним API;
підготовки початкової розмітки;
відображення статичного або майже статичного вмісту;
зменшення кількості JavaScript у браузері.
У Next.js компонент без директиви "use client" є Server Component:
// app/products/page.jsx
async function getProducts() {
const response = await fetch("https://fakestoreapi.com/products");
if (!response.ok) {
throw new Error("Не вдалося завантажити товари");
}
return response.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<main>
<h1>Товари</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.title} — {product.price} $
</li>
))}
</ul>
</main>
);
}У цьому прикладі:
ProductsPage виконується на сервері.
Дані завантажуються до формування інтерфейсу.
Клієнт отримує готовий результат.
Код getProducts не потрапляє до JavaScript-коду браузера.
Server Component може бути асинхронним, тому в ньому можна очікувати результат fetch за допомогою await.
Client Component виконується в браузері. Він потрібен тоді, коли інтерфейс має реагувати на дії користувача або використовувати можливості браузера.
Client Components використовують для:
обробників подій, наприклад onClick;
стану через useState;
ефектів через useEffect;
взаємодії з window, document або localStorage;
компонентів, які повинні змінюватися без нового запиту сторінки.
У Next.js Client Component позначається директивою "use client" на початку файлу:
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<section>
<p>Лічильник: {count}</p>
<button onClick={() => setCount(count + 1)}>
Збільшити
</button>
</section>
);
}Директива "use client" означає, що цей файл і його клієнтські залежності можуть виконуватися в браузері.
| Server Component | Client Component | | --- | --- | | Виконується на сервері | Виконується в браузері | | Може безпосередньо отримувати серверні дані | Може реагувати на дії користувача | | Не використовує useState і useEffect | Може використовувати useState і useEffect | | Не має обробників onClick у браузері | Підтримує обробники подій | | Не додає свій код до клієнтського JavaScript | Додає JavaScript, потрібний браузеру | | Підходить для отримання та відображення даних | Підходить для інтерактивної поведінки |
Server Component може відображати Client Component і передавати йому дані через props.
Файл сторінки:
// app/page.jsx
import Counter from "./Counter";
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<Counter initialValue={5} />
</main>
);
}Клієнтський компонент:
// app/Counter.jsx
"use client";
import { useState } from "react";
export default function Counter({ initialValue }) {
const [count, setCount] = useState(initialValue);
return (
<section>
<p>Поточне значення: {count}</p>
<button onClick={() => setCount(count + 1)}>
Збільшити
</button>
</section>
);
}У цьому випадку:
HomePage є Server Component.
Counter є Client Component.
Початкове значення 5 передається в Counter.
Подальша зміна лічильника відбувається у браузері.
Такий поділ дає змогу залишити більшу частину сторінки серверною, а на клієнт винести лише інтерактивну частину.
Поставте собі запитання: чи потрібен цій логіці браузер або дія користувача?
Виконуйте логіку на сервері, якщо вона:
отримує дані перед відображенням сторінки;
працює із серверним API;
читає дані з бази;
формує список, картку або інший вміст;
не потребує натискання кнопок чи введення даних у реальному часі;
не використовує браузерні об’єкти.
Наприклад, завантаження списку статей краще виконати на сервері:
async function ArticlesPage() {
const response = await fetch("https://example.com/api/articles");
const articles = await response.json();
return (
<ul>
{articles.map((article) => (
<li key={article.id}>{article.title}</li>
))}
</ul>
);
}
export default ArticlesPage;Виконуйте логіку в браузері, якщо вона:
змінює стан після натискання кнопки;
показує або приховує елементи;
обробляє введення у форму;
використовує useState або useEffect;
читає localStorage;
реагує на події браузера.
Наприклад, відкривання і закривання меню має виконуватися в Client Component:
"use client";
import { useState } from "react";
export default function Menu() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav>
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? "Закрити меню" : "Відкрити меню"}
</button>
{isOpen && (
<ul>
<li>Головна</li>
<li>Профіль</li>
<li>Налаштування</li>
</ul>
)}
</nav>
);
}У середовищах із підтримкою Server Components зазвичай варто:
залишати компонент Server Component за замовчуванням;
додавати "use client" лише там, де потрібна інтерактивність;
передавати в Client Component тільки необхідні дані;
розміщувати клієнтську межу якомога ближче до інтерактивного елемента.
Наприклад, сторінка товару може бути Server Component, а кнопка додавання в кошик — Client Component:
// app/products/[id]/page.jsx
import AddToCartButton from "./AddToCartButton";
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>{product.price} грн</p>
<AddToCartButton productId={product.id} />
</main>
);
}
async function getProduct(id) {
return {
id,
name: "Механічна клавіатура",
description: "Клавіатура для щоденної роботи",
price: 3200,
};
}// app/products/[id]/AddToCartButton.jsx
"use client";
import { useState } from "react";
export default function AddToCartButton({ productId }) {
const [added, setAdded] = useState(false);
function handleClick() {
// Тут може бути логіка оновлення стану кошика
console.log(`Товар ${productId} додано до кошика`);
setAdded(true);
}
return (
<button onClick={handleClick} disabled={added}>
{added ? "Додано" : "Додати до кошика"}
</button>
);
}Весь опис товару залишається серверним, а інтерактивною є лише кнопка.
Server Component може передавати Client Component прості дані через props:
рядки;
числа;
логічні значення;
масиви;
об’єкти зі звичайними значеннями.
// Server Component
import UserGreeting from "./UserGreeting";
export default function Page() {
const user = {
name: "Олена",
isPremium: true,
};
return <UserGreeting user={user} />;
}// Client Component
"use client";
export default function UserGreeting({ user }) {
return (
<p>
Вітаємо, {user.name}! Преміум-статус:{" "}
{user.isPremium ? "активний" : "неактивний"}.
</p>
);
}Не слід передавати в Client Component серверні секрети, підключення до бази даних або функції, які виконуються лише на сервері. Дані, передані в браузер, потенційно доступні користувачеві.
Такий код потребує браузера:
export default function Button() {
return <button onClick={() => alert("Натиснуто")}>Натиснути</button>;
}Якщо компонент має бути Client Component, у середовищі з підтримкою Server Components його потрібно позначити:
"use client";
export default function Button() {
return <button onClick={() => alert("Натиснуто")}>Натиснути</button>;
}useState призначений для компонентів, які працюють у браузері:
"use client";
import { useState } from "react";
export default function Toggle() {
const [enabled, setEnabled] = useState(false);
return (
<button onClick={() => setEnabled(!enabled)}>
{enabled ? "Увімкнено" : "Вимкнено"}
</button>
);
}Якщо додати "use client" до великого компонента, у браузер може потрапити більше JavaScript, ніж потрібно.
Краще розділити сторінку:
дані та основний вміст — Server Component;
кнопка, форма або перемикач — невеликий Client Component.
У багатьох середовищах Client Component може спочатку бути підготовлений або відрендерений на сервері, а потім «ожити» в браузері. Тому код компонента не повинен безпечно звертатися до window чи document під час початкового виконання без відповідної перевірки.
"use client" до кожного компонентаДиректива потрібна для компонентів, яким необхідні:
стан;
ефекти;
події;
браузерні API.
Звичайний текст, список або картку без інтерактивності часто можна залишити Server Component.
Не передавайте в Client Component:
паролі;
приватні ключі;
токени, які не повинні бути доступні браузеру;
серверні об’єкти підключення.
Client Component призначений для роботи в середовищі користувача.
Client Component не має напряму працювати з базою даних або серверними модулями. Для такої взаємодії потрібен серверний механізм, наприклад API-обробник, але спосіб його створення залежить від конкретного React-фреймворку.
Скористайтеся таким алгоритмом:
Чи потрібен компоненту useState, useEffect або обробники подій?
Так — використовуйте Client Component.
Ні — переходьте до наступного запитання.
Чи потрібно отримувати дані із сервера до відображення?
Так — використовуйте Server Component.
Ні — компонент може залишатися Server Component.
Чи використовує код window, document або localStorage?
Так — використовуйте Client Component.
Ні — Server Component часто буде достатнім.
Чи є інтерактивною лише невелика частина компонента?
Так — винесіть її в окремий Client Component.
Не робіть всю сторінку клієнтською без потреби.
Server Component виконується на сервері й добре підходить для отримання та відображення даних.
Client Component виконується в браузері й потрібен для стану, подій та браузерних API.
У середовищах на кшталт Next.js App Router компоненти за замовчуванням є Server Components.
Директива "use client" позначає межу Client Component.
Server Component може містити Client Component і передавати йому дані через props.
Інтерактивну логіку варто ізолювати в невеликих Client Components.
Серверну логіку та секретні дані не можна переносити в браузер.