Серверные и клиентские компоненты
В App Router каждый компонент по умолчанию серверный: он выполняется на сервере, а в браузер уходит только готовый HTML. Компонент с состоянием, эффектами или обработчиками событий должен работать в браузере — такой компонент называют клиентским и помечают директивой 'use client'.
Серверный компонент
Все страницы курса до сих пор были серверными. Поэтому страница статьи могла быть async и вызывать await getPost(slug):
// app/posts/[slug]/page.tsx
import { getPost } from '@/lib/posts';
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
console.log('Читаем статью', slug);
return <h1>{post?.title}</h1>;
}Откройте /posts/hello-nextjs: сообщение «Читаем статью» появится в терминале, где запущен npm run dev, а не в консоли браузера. Код этого компонента в браузер не попадает.
Клиентский компонент
Кнопка «Нравится» хранит счётчик в состоянии, значит, ей нужен браузер. Создайте файл components/LikeButton.tsx:
// components/LikeButton.tsx
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0);
return <button onClick={() => setLikes(likes + 1)}>Нравится: {likes}</button>;
}Директива 'use client' стоит в самой первой строке файла, до импортов. Она говорит Next.js: этот компонент и всё, что он импортирует, нужно отправить в браузер.
Как их сочетать
Серверный компонент может отрисовать клиентский:
// app/posts/[slug]/page.tsx (фрагмент)
import LikeButton from '@/components/LikeButton';
// ...внутри разметки статьи
<LikeButton />На странице статьи появится кнопка, и счётчик будет расти при нажатии. Сама статья отрисована на сервере, в браузере оживает только кнопка.
Что где можно
| Возможность | Серверный | Клиентский |
|---|---|---|
async и await в компоненте | Да | Нет |
| Чтение базы данных и секретов | Да | Нет |
useState, useEffect | Нет | Да |
onClick, onChange | Нет | Да |
window, localStorage | Нет | Да |
Передача данных
Серверный компонент передаёт клиентскому данные через свойства: <LikeButton initial={10} />. Значения должны превращаться в JSON — строки, числа, массивы, обычные объекты. Функцию передать нельзя, исключение — серверные действия из отдельного урока.
Сначала на сервере
Клиентский компонент тоже сначала отрисовывается на сервере в HTML, а затем в браузере React «оживляет» его — подключает обработчики. Этот процесс называют гидратацией.
Частая ошибка
Добавлять 'use client' в каждый файл «на всякий случай». Тогда весь код, включая работу с данными, уезжает в браузер, страница грузится дольше, а async-компоненты перестают работать. Помечайте только небольшие интерактивные части вроде кнопок и форм, а страницы оставляйте серверными.