Next.js 15: Полное руководство по Server Components

В 2026 году 78% новых Next.js проектов используют App Router с Server Components. Это не тренд — это новая парадигма фулстек-разработки, которая меняет подход к производительности, SEO и архитектуре React-приложений.
Что вы узнаете:
- ✅ Как работает гидратация и почему RSC её устраняют
- ✅ Паттерны разделения Client/Server компонентов
- ✅ Кэширование и стриминг в Next.js 15
- ✅ Миграция с Pages Router на App Router
1. Архитектура Server Components
React Server Components (RSC) — это компоненты, которые рендерятся исключительно на сервере. Они никогда не попадают в клиентский бандл, что даёт три ключевых преимущества:
| Характеристика | Server Component | Client Component |
|---|---|---|
| Где выполняется | Только сервер (Node.js/Edge) | Сервер + Браузер |
| Размер бандла | 0 KB (не отправляется клиенту) | Весь код компонента |
| Доступ к БД/API | ✅ Прямой доступ | ❌ Только через fetch/API routes |
| Интерактивность | ❌ Нет useState/useEffect | ✅ Полная интерактивность |
| SEO | ✅ Полный HTML в ответе | ⚠️ Требует SSR/SSG |
💡 Совет: По умолчанию все компоненты в App Router — Server Components. Чтобы сделать компонент клиентским, добавьте директиву 'use client' в начало файла. Не используйте её без необходимости — каждый клиентский компонент увеличивает размер JavaScript-бандла.
2. Гидратация и Zero-Bundle-Size
Традиционный SSR (Pages Router) работает по схеме: рендер на сервере → отправка HTML → загрузка JS → гидратация → интерактивность. Проблема в том, что React должен «оживить» весь HTML, сравнивая виртуальный DOM с реальным.
С Server Components этот процесс меняется радикально:
// app/page.tsx — Server Component по умолчанию
import { db } from '@/lib/db' // ✅ Директ импорт на сервере
export default async function ProductPage() {
// ✅ Прямой запрос к БД без API слоя
const products = await db.query('SELECT * FROM products')
return (
Каталог товаров
{products.map(p => (
))}
)
}
В этом примере db.query выполняется только на сервере. Клиент получает готовый HTML без кода для работы с базой данных. Экономия: ~15-40 KB JavaScript на типичной странице каталога.
⚠️ Важно: Server Components не могут использовать браузерные API (window, document, localStorage) и React-хуки состояния (useState, useEffect, useContext). Для интерактивных элементов создавайте клиентские компоненты и импортируйте их в Server Components.
3. Паттерны разделения компонентов
Граница между сервером и клиентом — ключевое архитектурное решение. Вот проверенные паттерны из production-проектов 2026 года:
Паттерн «Листья клиента»
Делайте Server Components «стволом» дерева, а интерактивные элементы — «листьями»:
// app/product/[id]/page.tsx — Server Component
import { AddToCartButton } from './AddToCartButton' // Client Component
import { db } from '@/lib/db'
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await db.products.findUnique({ where: { id: params.id } })
return (
{product.name}
{product.description}
{/* ✅ Только кнопка — клиентский компонент */}
)
}
Паттерн «Составной компонент»
Передавайте Server Components как children в Client Components:
// components/Modal.tsx — Client Component
'use client'
export function Modal({ children, trigger }: { children: React.ReactNode, trigger: React.ReactNode }) {
const [open, setOpen] = useState(false)
return (
<>
{open && {children}}
)
}
// app/page.tsx — Server Component
import { Modal } from '@/components/Modal'
import { ProductList } from './ProductList' // Server Component
export default function Page() {
return (
{/* ✅ ProductList рендерится на сервере, но показывается в клиентском модале */}
)
}
💡 Совет: Используйте анализатор бандла @next/bundle-analyzer, чтобы увидеть, какие компоненты попадают в клиентский чанк. Цель: минимизировать 'use client' директивы на верхних уровнях дерева.
4. Кэширование и Partial Prerendering
Next.js 15 принёс революционную функцию — Partial Prerendering (PPR). Это гибрид статической генерации и динамического стриминга:
// next.config.js
module.exports = {
experimental: {
ppr: true, // Включаем Partial Prerendering
},
}
// app/page.tsx
import { Suspense } from 'react'
import { StaticHeader } from './StaticHeader' // Статический компонент
import { DynamicReviews } from './DynamicReviews' // Динамический компонент
export default function Page() {
return (
<>
{/* ✅ Предрендерится при сборке */}
{/* 🔄 Стримится при запросе с fallback */}
}>
)
}
Результат: Time to First Byte (TTFB) снижается на 60-80% по сравнению с чистым SSR, а пользователи видят контент мгновенно.
| Стратегия | Когда использовать | TTFB |
|---|---|---|
| Static Generation | Контент не меняется (документация, лендинги) | ~50ms (CDN) |
| Partial Prerendering | Смешанный контент (e-commerce, блоги) | ~100-200ms |
| Dynamic Streaming | Персонализированный контент (дашборды) | ~200-500ms |
| Traditional SSR | Legacy Pages Router проекты | ~300-800ms |
5. Миграция с Pages Router
По данным опроса State of React 2026, 64% компаний уже мигрировали или находятся в процессе перехода на App Router. Вот пошаговый план:
// 1. Параллельная маршрутизация (существующий код работает)
app/
├── (marketing)/ # Новые страницы на App Router
│ ├── page.tsx
│ └── about/page.tsx
├── api/ # API Routes остаются в pages/api/
└── ...
pages/ # Старые страницы работают без изменений
├── index.tsx
├── about.tsx
└── api/
Этап 1: Создайте папку app/(marketing) для новых страниц. Скобки в названии исключают сегмент из URL.
Этап 2: Переносите страницы по одной, начиная с простых (статический контент).
Этап 3: Для сложных страниц используйте инкрементальный перенос компонентов.
⚠️ Важно: getServerSideProps и getStaticProps не работают в App Router. Данные запрашивайте напрямую в компонентах через fetch или ORM. Для кэширования используйте fetch('/api', { next: { revalidate: 60 } }) или unstable_cache.
6. Production-готовые паттерны
Обработка ошибок
// app/error.tsx — Error Boundary для сегмента
'use client'
export default function ErrorBoundary({ error, reset }: { error: Error, reset: () => void }) {
return (
Что-то пошло не так
)
}
// app/not-found.tsx — 404 страница
export default function NotFound() {
return (
Страница не найдена
На главную
)
}
Loading состояния
// app/loading.tsx — Показывается автоматически при загрузке
export default function Loading() {
return Загрузка...
}
// Или гранулярно с Suspense
import { Suspense } from 'react'
}>
💡 Совет: Используйте loading.tsx для немедленной обратной связи при навигации. Next.js автоматически показывает его при переходе между страницами, что улучшает воспринимаемую производительность (perceived performance).
Готовы перейти на Next.js 15?
Мы помогаем командам мигрировать на App Router без downtime. Аудит текущей архитектуры, поэтапный перенос, оптимизация Core Web Vitals.
Заключение
Next.js 15 с Server Components — это не просто новая версия фреймворка, а фундаментальный сдвиг в архитектуре веб-приложений. Устранение гидратации, zero-bundle-size компоненты и Partial Prerendering дают реальные бизнес-преимущества: скорость, SEO, снижение инфраструктурных затрат.
Ключевые принципы для production:
- Server First — начинайте с Server Components, добавляйте клиентскую интерактивность только где нужно
- Гранулярность — чем ниже в дереве 'use client', тем меньше бандл
- PPR — используйте Partial Prerendering для сложных страниц
- Инкрементальность — мигрируйте постепенно, страница за страницей
Следующая эра React уже здесь. Не оставайтесь на Pages Router — будущее за Server Components! 🚀
Pavel Levdin
Full-Stack разработчик с 10+ годами опыта. Специализируюсь на React, Next.js и современной веб-разработке.


