Как работает Server-Side Rendering (SSR) в Next.js
SSR (Server-Side Rendering) это стратегия, при которой HTML генерируется на сервере при каждом запросе пользователя. В отличие от SSG, страница не создается заранее, а формируется в реальном времени.
Как это работает
Пользователь делает запрос
Браузер отправляет запрос на сервер. Например, пользователь открывает страницу профиля на Hack Frontend.
Сервер рендерит страницу
Next.js выполняет серверный компонент: обращается к базе данных, проверяет авторизацию, собирает данные и генерирует HTML.
HTML отправляется клиенту
Пользователь получает готовый HTML с актуальными данными. Затем React гидратирует страницу на клиенте, делая ее интерактивной.
SSR в App Router
В App Router SSR включается, когда компонент использует динамические данные. Достаточно указать cache: 'no-store' в fetch-запросе или использовать динамические функции (cookies(), headers()):
// app/profile/page.tsx
import { cookies } from 'next/headers'
import { db } from '@/lib/db'
export default async function ProfilePage() {
const session = cookies().get('session')
if (!session) return <LoginPrompt />
const user = await db.user.findUnique({
where: { sessionId: session.value }
})
return (
<div>
<h1>Привет, {user.name}!</h1>
<p>Решено задач: {user.solvedProblems.length}</p>
</div>
)
}
Использование cookies() автоматически делает эту страницу динамической (SSR). Next.js понимает, что результат зависит от запроса и не может быть закеширован.
Явное указание динамического рендеринга
Можно явно отключить кеширование для fetch-запросов:
// app/feed/page.tsx
export default async function FeedPage() {
const res = await fetch('https://api.hackfrontend.com/feed', {
cache: 'no-store'
})
const posts = await res.json()
return <FeedList posts={posts} />
}
Или задать режим на уровне всего сегмента роута:
// app/dashboard/layout.tsx
export const dynamic = 'force-dynamic'
export default function DashboardLayout({
children
}: {
children: React.ReactNode
}) {
return <div className="dashboard">{children}</div>
}
SSR в Pages Router (Legacy)
В Pages Router для SSR используется getServerSideProps:
// pages/profile.tsx
export async function getServerSideProps(context) {
const { req } = context
const session = req.cookies.session
if (!session) {
return { redirect: { destination: '/auth/login', permanent: false } }
}
const user = await db.user.findUnique({
where: { sessionId: session }
})
return { props: { user } }
}
export default function ProfilePage({ user }) {
return (
<div>
<h1>Привет, {user.name}!</h1>
<p>Решено задач: {user.solvedProblems.length}</p>
</div>
)
}
Когда использовать SSR
Подходит:
- Страницы с персонализацией (профиль, настройки, дашборд)
- Контент, зависящий от cookies или заголовков (авторизация, A/B тесты)
- Данные, которые должны быть актуальными на момент запроса
- Страницы с часто меняющимся контентом, где ISR недостаточно
Не подходит:
- Статический контент (документация, блог). SSG будет быстрее и дешевле.
- Страницы, где допустимо показать данные с задержкой. ISR будет эффективнее.
SSR vs SSG
| SSG | SSR | |
|---|---|---|
| Когда генерируется | При сборке | При каждом запросе |
| Скорость ответа | Мгновенная (CDN) | Зависит от сервера |
| Актуальность данных | На момент сборки | Всегда актуальные |
| Нагрузка на сервер | Нет | При каждом запросе |
| Персонализация | Нет | Да |
Важно:
SSR увеличивает Time to First Byte (TTFB), потому что сервер тратит время на рендеринг перед отправкой ответа. Если страница делает тяжелые запросы к БД, пользователь будет ждать. Используй SSR осознанно, а не по умолчанию.
Потоковый рендеринг (Streaming)
В App Router Next.js поддерживает потоковую передачу HTML. Это позволяет отправлять части страницы по мере их готовности, не дожидаясь завершения всех запросов:
// app/dashboard/page.tsx
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<div>
<h1>Дашборд</h1>
<Suspense fallback={<p>Загрузка статистики...</p>}>
<UserStats />
</Suspense>
<Suspense fallback={<p>Загрузка активности...</p>}>
<RecentActivity />
</Suspense>
</div>
)
}
Пользователь увидит заголовок и фоллбеки сразу, а данные подгрузятся по мере готовности. Это значительно улучшает воспринимаемую скорость.
Полезные ресурсы
- nextjs.org/docs — динамический рендеринг
- Streaming and Suspense — потоковый рендеринг