Next.js vs SPA: когда серверный рендеринг действительно нужен.

Разбираемся, когда серверный рендеринг в Next.js даёт реальное преимущество, а когда обычный SPA на Vite+React - лучший выбор.

О чём эта статья и зачем её читать

Когда компания решает сделать сайт или веб-приложение, одним из первых встаёт вопрос: какую технологию использовать? В мире React (самой популярной библиотеки для создания сайтов) есть два основных подхода - SPA и SSR. За этими аббревиатурами стоят совершенно разные способы показать вам сайт. И от выбора зависит, насколько быстро сайт будет загружаться, увидят ли его поисковики и сколько будет стоить хостинг.

Давайте разберёмся в этом простым языком, без лишнего занудства. Даже если вы не программист - после этой статьи вы поймёте, что к чему, и сможете осознанно обсуждать это с разработчиками.

Аналогия: ресторан и конструктор

Представьте два ресторана.

В первом (это SSR - серверный рендеринг) повар готовит блюдо на кухне и приносит вам готовое. Вы открываете крышку - и сразу видите красивый стейк с гарниром. Можно сразу есть.

Во втором ресторане (это SPA - одностраничное приложение) вам приносят поднос с ингредиентами и инструкцию. Вы сами собираете блюдо за столом. Первый раз ждёте дольше (пока всё приготовите), но потом, если хотите добавку или изменить гарнир - всё происходит мгновенно, без ожидания кухни.

Оба подхода кормят вас. Но работают по-разному, и каждый лучше в своей ситуации.

Что такое SPA (одностраничное приложение)

SPA расшифровывается как Single Page Application - «одностраничное приложение». Это значит, что весь сайт - это технически одна HTML-страница. Когда вы первый раз заходите на такой сайт, браузер (программа, через которую вы смотрите сайты - Chrome, Safari, Firefox) скачивает весь код сайта целиком. После этого переходы между страницами происходят мгновенно - браузер просто перерисовывает содержимое, не обращаясь к серверу за новой страницей.

Как это выглядит для пользователя:

  1. Вы вводите адрес сайта в браузере
  2. Браузер получает почти пустую страницу (буквально белый экран на долю секунды)
  3. Скачивается JavaScript-код (программа, которая построит сайт)
  4. Код выполняется и рисует интерфейс - вы видите сайт
  5. Дальше всё работает молниеносно - клики, переходы, анимации
// Это упрощённый пример SPA-приложения
// Не пугайтесь кода - просто посмотрите на комментарии

// Подключаем нужные инструменты из React
import { lazy, Suspense } from 'react'

// Загружаем страницы "лениво" - только когда пользователь на них перейдёт
// Это экономит трафик: зачем скачивать страницу "О нас",
// если пользователь смотрит только главную?
const Home = lazy(() => import('./pages/Home'))
const Products = lazy(() => import('./pages/Products'))
const About = lazy(() => import('./pages/About'))

// Описываем маршруты - какой адрес какой странице соответствует
// "/" - главная страница
// "/products" - каталог товаров
// "/about" - страница "О нас"
const routes = [
  { path: '/', element: <Home /> },
  { path: '/products', element: <Products /> },
  { path: '/about', element: <About /> },
]

// Пока страница загружается - показываем надпись "Загрузка..."
// Это тот самый момент, когда пользователь видит пустой экран
function App() {
  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <RouterProvider routes={routes} />
    </Suspense>
  )
}

Плюсы SPA:

  • После первой загрузки всё работает очень быстро - страницы переключаются мгновенно
  • Приятный пользовательский опыт - нет перезагрузок страницы
  • Простой и дешёвый хостинг - это просто набор файлов, можно положить куда угодно
  • Проще разрабатывать - меньше сложностей с сервером

Минусы SPA:

  • Первая загрузка медленнее - нужно скачать весь код
  • Поисковые роботы (Google, Яндекс) могут не увидеть содержимое - они получают пустую страницу
  • Пока код не загрузится - пользователь видит белый экран

Что такое SSR (серверный рендеринг)

SSR расшифровывается как Server-Side Rendering - «рендеринг на стороне сервера». Сервер - это мощный компьютер, на котором живёт ваш сайт. При SSR этот компьютер сам формирует готовую страницу и отправляет её браузеру. Браузер получает уже готовый HTML (код страницы) с текстом, картинками и всем содержимым.

Как это выглядит для пользователя:

  1. Вы вводите адрес сайта
  2. Сервер формирует готовую страницу со всем содержимым
  3. Браузер получает готовый HTML и сразу показывает контент
  4. Параллельно загружается JavaScript, который делает страницу интерактивной (кнопки начинают работать)

Next.js - это самый популярный инструмент для создания сайтов с SSR. Он построен на React и добавляет серверный рендеринг «из коробки» (то есть без дополнительных настроек).

// Пример страницы на Next.js с серверным рендерингом
// Этот код выполняется на СЕРВЕРЕ, а не в браузере пользователя

// Функция для страницы товара
// params.id - это номер товара из адресной строки
// Например, для адреса /products/42 params.id будет "42"
export default async function ProductPage({ params }) {
  // Сервер сам запрашивает данные о товаре из базы данных
  // Пользователь этого не видит - всё происходит на сервере
  const product = await getProductFromDatabase(params.id)

  // Сервер формирует готовый HTML и отправляет его пользователю
  // Пользователь сразу видит название и описание товара
  return (
    <div>
      <h1>{product.name}</h1>
      <p>Цена: {product.price} руб.</p>
      <p>{product.description}</p>
    </div>
  )
}

// Метаданные для поисковиков - тоже генерируются на сервере
// Это то, что Google показывает в результатах поиска
export async function generateMetadata({ params }) {
  const product = await getProductFromDatabase(params.id)

  return {
    // Заголовок страницы - появится во вкладке браузера и в Google
    title: product.name + ' - купить в нашем магазине',
    // Описание - Google покажет его под заголовком
    description: product.shortDescription,
  }
}

А ещё есть SSG и ISR

Помимо SPA и SSR существуют ещё два подхода, и они тоже стоят внимания:

SSG (Static Site Generation) - статическая генерация. Все страницы создаются заранее, на этапе сборки сайта. Это как напечатать каталог в типографии - все страницы готовы заранее, их остаётся только раздать. Идеально для блогов, документации и лендингов, где содержимое меняется редко.

ISR (Incremental Static Regeneration) - инкрементальная статическая регенерация. Звучит сложно, но идея простая: страницы генерируются статически (как в SSG), но автоматически обновляются через заданный интервал. Это как каталог, который сам перепечатывает устаревшие страницы. Вы получаете скорость статики и актуальность данных.

// Пример ISR в Next.js
// Эта страница будет обновляться каждые 60 секунд

// revalidate = 60 означает:
// "Покажи кэшированную версию, но через 60 секунд обнови её"
export const revalidate = 60

export default async function PricePage() {
  // Цены загружаются из базы данных
  const prices = await getPrices()

  return (
    <div>
      <h1>Наши цены</h1>
      <p>Лендинг: от {prices.landing} руб.</p>
      <p>Интернет-магазин: от {prices.shop} руб.</p>
      <p>Обновлено: {new Date().toLocaleString()}</p>
    </div>
  )
}
// Первый посетитель получит свежую страницу
// Следующие 60 секунд все получают кэшированную (мгновенную) версию
// Через 60 секунд страница обновится в фоне

Когда серверный рендеринг реально нужен

SSR - это не «всегда лучше». Это инструмент для конкретных задач. Вот когда он действительно необходим:

1. Когда важно, чтобы сайт находили в Google и Яндексе

Поисковые роботы - это программы, которые обходят интернет и собирают информацию о сайтах. Они заходят на вашу страницу и смотрят, что там написано. Проблема в том, что SPA-сайт при первом заходе показывает пустую страницу. Да, через секунду JavaScript нарисует всё содержимое, но робот может этого не дождаться.

Google научился ждать и выполнять JavaScript, но делает это с задержкой (иногда несколько дней). Если у вас интернет-магазин с тысячами товаров - каждый день задержки индексации это потерянные клиенты. SSR решает эту проблему: робот получает готовую страницу со всем содержимым сразу.

2. Когда скорость загрузки критична для бизнеса

Исследования показывают: каждая лишняя секунда загрузки уменьшает конверсию на 7%. Для интернет-магазина с оборотом 10 млн рублей в месяц одна секунда задержки - это 700 000 рублей потерь. SSR даёт выигрыш в 0.5-1.5 секунды по сравнению с SPA, потому что пользователь видит контент раньше.

3. Когда контент зависит от пользователя

Если нужно показать разные цены для разных регионов, персональные рекомендации или результаты A/B-тестов с первого кадра - SSR позволяет сформировать нужную версию страницы на сервере, до того как пользователь её увидит.

Когда SPA - лучший выбор

А вот ситуации, когда SSR не нужен и только добавляет сложности:

1. Админ-панели и внутренние инструменты

Админка интернет-магазина, CRM-система, корпоративный портал - всё это закрыто за логином. Поисковики туда не зайдут, а сотрудники готовы подождать пару секунд первой загрузки. Зато потом они работают в быстром интерфейсе, который не перезагружается при каждом действии.

2. Приложения, где пользователь проводит много времени

Если человек работает в вашем приложении часами (бухгалтерская программа, редактор документов, система управления проектами) - первая загрузка не так важна. Важно, чтобы дальше всё летало. SPA здесь идеален.

3. Прототипы и MVP

Когда нужно быстро проверить бизнес-идею, SPA на Vite (быстрый инструмент для сборки сайтов) разворачивается за минуты. Добавлять серверный рендеринг на этом этапе - трата времени. Если идея выстрелит - перейдёте на Next.js позже.

Что такое гидрация и почему с ней бывают проблемы

Гидрация (от слова «hydrate» - наполнять водой) - это процесс, при котором статичная HTML-страница «оживает». Представьте музей восковых фигур. SSR создаёт красивые фигуры (HTML), а гидрация превращает их в живых людей (добавляет интерактивность - кнопки начинают нажиматься, формы начинают отправляться).

Проблема возникает, когда то, что сервер нарисовал, отличается от того, что браузер пытается нарисовать. Это как если бы восковая фигура была блондинкой, а «живая версия» - брюнеткой. React видит несовпадение и ругается.

// Пример проблемы с гидрацией

// ПЛОХО: сервер и браузер покажут разное время
// На сервере сейчас 10:00 (серверное время)
// У пользователя в Москве - 15:00
// React видит несовпадение и выдаёт ошибку
function Clock() {
  return <p>Сейчас {new Date().toLocaleTimeString()}</p>
}

// ХОРОШО: показываем время только после загрузки в браузере
function Clock() {
  // Сначала показываем пустое значение (одинаково на сервере и клиенте)
  const [time, setTime] = useState('')

  // useEffect запускается ТОЛЬКО в браузере, после загрузки
  // Сервер этот код не выполняет
  useEffect(() => {
    setTime(new Date().toLocaleTimeString())
    // Обновляем каждую секунду
    const timer = setInterval(() => {
      setTime(new Date().toLocaleTimeString())
    }, 1000)
    return () => clearInterval(timer)
  }, [])

  return <p>{time ? `Сейчас ${time}` : 'Загружаем часы...'}</p>
}

Серверные и клиентские компоненты в Next.js

В современном Next.js (версия 13 и выше) появилось важное нововведение: компоненты бывают серверные и клиентские.

Серверный компонент - работает только на сервере. Может напрямую обращаться к базе данных, читать файлы на сервере. Его код не отправляется в браузер пользователя, что экономит трафик. Это как повар, который работает на кухне - вы видите только готовое блюдо.

Клиентский компонент - работает в браузере. Нужен для всего интерактивного: кнопки, формы, анимации, всё что реагирует на действия пользователя. Отмечается специальной строкой 'use client' в начале файла.

// Серверный компонент (по умолчанию в Next.js)
// Этот код НИКОГДА не попадёт в браузер пользователя
// Он выполняется на сервере и отправляет только готовый HTML

async function ProductList() {
  // Можно напрямую обращаться к базе данных!
  // В обычном SPA так нельзя - браузер не имеет доступа к базе
  const products = await db.query('SELECT * FROM products')

  return (
    <ul>
      {products.map(p => (
        <li key={p.id}>{p.name} - {p.price} руб.</li>
      ))}
    </ul>
  )
}

// Клиентский компонент - работает в браузере
// Строка 'use client' говорит: "этот код нужен в браузере"
'use client'

function AddToCartButton({ productId }) {
  // useState - хранит состояние (загружается кнопка или нет)
  const [loading, setLoading] = useState(false)

  // Эта функция выполнится, когда пользователь нажмёт кнопку
  const handleClick = async () => {
    setLoading(true)  // Показываем что идёт загрузка

    // Отправляем запрос на сервер - "добавь этот товар в корзину"
    await fetch('/api/cart', {
      method: 'POST',
      body: JSON.stringify({ productId }),
    })

    setLoading(false)  // Загрузка завершена
  }

  return (
    <button onClick={handleClick}>
      {loading ? 'Добавляем...' : 'В корзину'}
    </button>
  )
}

Сравнение скорости

Мы протестировали один и тот же сайт-каталог (50 товаров на главной, карточки с картинками) в разных режимах. Тестировали на медленном мобильном интернете (3G), потому что именно на слабом соединении разница видна лучше всего:

Что измеряемSPA (Vite)SSR (Next.js)SSG (Next.js)
Когда пользователь увидит первый контент3.2 сек1.4 сек0.8 сек
Когда загрузится главный элемент4.8 сек2.1 сек1.2 сек
Когда можно кликать по кнопкам5.1 сек3.8 сек2.5 сек
Размер скачиваемого кода285 КБ210 КБ180 КБ

Как видите, SSR и SSG показывают контент в 2-3 раза быстрее. Но учтите: после первой загрузки SPA работает быстрее при навигации между страницами (нет обращений к серверу).

Разница в хостинге и стоимости

Это важный практический момент, который часто забывают:

SPA - это просто набор файлов (HTML, CSS, JavaScript). Их можно положить на любой хостинг, даже самый дешёвый. Стоимость: от 0 до 500 рублей в месяц. Никаких серверов, никаких процессов - просто файлы, которые отдаются посетителям.

Next.js с SSR - это полноценная серверная программа, которая должна работать 24/7. Нужен VPS (виртуальный сервер) или облачный хостинг. Стоимость: от 500 до 5000 рублей в месяц. Нужен мониторинг, нужно следить чтобы процесс не упал.

Как выбрать: простая шпаргалка

Ответьте на эти вопросы - и выбор станет очевидным:

  • Сайт должен хорошо находиться в Google/Яндексе? Да - Next.js. Нет (внутренний инструмент) - SPA
  • Контент одинаковый для всех? Да и меняется редко - SSG. Да но меняется часто - ISR. Разный для разных пользователей - SSR
  • Это приложение за логином? Да - SPA
  • Важна каждая десятая секунды загрузки? Да (e-commerce, медиа) - SSR/SSG. Нет - SPA
  • Бюджет ограничен? SPA дешевле в хостинге. Next.js дороже но мощнее

Главная ошибка - выбирать технологию потому что она «модная». Next.js отлично подходит для контентных сайтов и интернет-магазинов. Vite+React - для приложений за логином. Это не конкуренты, это инструменты для разных задач.

Практический пример: когда мы выбираем Next.js для клиентов

Вот реальные сценарии из нашей практики:

Проект 1: Интернет-магазин одежды

Клиент пришёл с SPA-сайтом на React. Проблема: Google не индексировал страницы товаров, потому что они рендерились на клиенте. Трафик из поиска - 0. Мы перевели на Next.js с SSG (статической генерацией) для страниц товаров. Через 2 месяца органический трафик вырос на 400%.

Проект 2: Дашборд для аналитики

Клиент хотел "красивый дашборд для менеджеров". SEO не нужен (сайт за авторизацией), контент динамический (графики обновляются каждые 5 секунд). Мы оставили SPA на React + Vite - быстрее в разработке, дешевле в поддержке, и для этой задачи SSR не даёт никаких преимуществ.

Проект 3: Блог с высокой посещаемостью

Клиент - медиа-компания, 500+ статей, 100 000 посетителей в месяц. Выбрали Next.js с ISR (инкрементальной статической регенерацией): страницы статей генерируются один раз и кэшируются, обновляются каждые 60 секунд. Результат: время загрузки 0.3 секунды, нагрузка на сервер минимальная.

Советы по миграции: как перейти с SPA на Next.js

Если вы решили перевести существующий SPA на Next.js, вот пошаговый план:

// Шаг 1: Начните с маршрутизации
// В SPA маршруты обычно описаны в одном файле (react-router)
// В Next.js - файловая маршрутизация (каждый файл = маршрут)

// Было (React Router):
// src/App.tsx
// <Routes>
//   <Route path="/" element={<Home />} />
//   <Route path="/products" element={<Products />} />
//   <Route path="/products/:id" element={<ProductDetail />} />
// </Routes>

// Стало (Next.js App Router):
// src/app/page.tsx             - главная страница (/)
// src/app/products/page.tsx    - список товаров (/products)
// src/app/products/[id]/page.tsx - страница товара (/products/123)

// Шаг 2: Определите, какие страницы нуждаются в SSR
// Не все! Только те, которым нужен SEO или быстрая первая загрузка

// Страницы для SSR/SSG (нужен SEO):
// - Главная, каталог, страницы товаров, блог, о компании

// Страницы для CSR (не нужен SEO):
// - Личный кабинет, корзина, оформление заказа, админ-панель

// Шаг 3: Разделите компоненты на серверные и клиентские
// По умолчанию в Next.js 15 все компоненты - серверные
// Добавьте 'use client' только там, где нужна интерактивность

// Серверный компонент (по умолчанию):
// Рендерится на сервере, не отправляет JavaScript клиенту
export default async function ProductPage({ params }) {
  // Этот запрос к БД выполняется на сервере!
  // Клиент НЕ видит строку подключения к базе данных
  const product = await prisma.product.findUnique({
    where: { id: params.id }
  })

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      {/* AddToCartButton - клиентский компонент (интерактивный) */}
      <AddToCartButton productId={product.id} />
    </div>
  )
}

// Клиентский компонент (с 'use client'):
// 'use client'
// import { useState } from 'react'
// export function AddToCartButton({ productId }) {
//   const [added, setAdded] = useState(false)
//   return <button onClick={() => { addToCart(productId); setAdded(true) }}>
//     {added ? 'В корзине' : 'В корзину'}
//   </button>
// }

Миграция не обязательно должна быть "всё или ничего". Вы можете переводить страницы постепенно: начните с тех, где SEO критичен (главная, каталог), а личный кабинет оставьте как SPA-компоненты с 'use client'.

Итого

SSR (серверный рендеринг) - это когда сервер готовит страницу целиком и отдаёт её браузеру готовой. Пользователь видит контент быстрее, поисковики видят всё содержимое. SPA (одностраничное приложение) - это когда браузер сам строит страницу из скачанного кода. Первая загрузка медленнее, но потом всё работает молниеносно.

Для бизнеса важно понимать: если ваш сайт должны находить клиенты через поиск и скорость загрузки влияет на продажи - выбирайте SSR (Next.js). Если вы делаете внутренний инструмент или приложение для постоянных пользователей - SPA будет проще и дешевле.

Не знаете, что подойдёт именно вам? Напишите нам - поможем разобраться и выбрать оптимальное решение для вашего проекта.

Все статьи
Next.js vs SPA: когда серверный рендеринг действительно нужен | Enot Software