1. Суть работы App Router
App Router — это новая система маршрутизации в Next.js, которая полностью строится на структуре папки app/. Каждый подкаталог внутри app/ становится маршрутом, а файлы вроде page.tsx и layout.tsx определяют, что именно будет показано пользователю.
"Маршруты = папки и файлы"
- /app/page.tsx → / (главная страница)
- /app/about/page.tsx → /about
- /app/blog/[slug]/page.tsx → /blog/что-угодно
Next.js сканирует папку app/ и строит дерево маршрутов. Каждый маршрут может иметь свою разметку (layout.tsx), шаблон (template.tsx), страницу (page.tsx), обработчики ошибок, загрузки и т.д.
App Router — это не просто навигация
App Router — это не только про "куда перейти". Это про то, как и где будет рендериться ваш компонент: на сервере, на клиенте или и там, и там. В эпоху Next.js 15 это становится особенно важным, ведь теперь можно комбинировать серверные и клиентские компоненты, а значит, управлять производительностью и интерактивностью на новом уровне.
2. Рендеринг: сервер, клиент и их гибриды
Серверный рендеринг (Server-side rendering, SSR)
Это когда страница или компонент собирается на сервере, и уже готовый HTML прилетает в браузер. Браузер быстро показывает пользователю страницу, а потом подключается JavaScript для оживления интерактивных элементов (если они есть).
- Плюсы:
- Быстрый первый рендер (особенно для поисковиков и соцсетей).
- Отлично подходит для SEO.
- Можно сразу показывать данные, полученные с сервера.
- Минусы:
- Интерактивность появляется чуть позже (после "гидратации").
- Каждый переход между страницами может требовать нового запроса к серверу.
Клиентский рендеринг (Client-side rendering, CSR)
В этом случае сервер отдаёт минимальный HTML и большой JS-бандл. Всё остальное происходит в браузере: React сам собирает виртуальное DOM-дерево, сам запрашивает данные, сам всё рисует.
- Плюсы:
- Молниеносная навигация после загрузки первого бандла.
- Можно делать очень динамичные, "живые" интерфейсы.
- Минусы:
- Первый рендер медленнее (особенно на слабых устройствах).
- SEO работает хуже (поисковик может не увидеть контент).
Гибридный рендеринг (Server + Client)
Next.js позволяет комбинировать оба подхода: часть компонентов рендерится на сервере, часть — только на клиенте. Это позволяет получить лучшее из обоих миров: быстрый первый рендер, хороший SEO, и при этом — интерактивность и динамику на клиенте.
В App Router это реализовано через Server Components и Client Components.
3. Как Next.js решает: где рендерить компонент?
В Next.js 15 по умолчанию все компоненты в папке app/ — серверные. Это значит, что они рендерятся на сервере, а в браузер отправляется уже готовый HTML. Если компоненту нужна интерактивность (например, обработка клика или состояние), его нужно явно сделать клиентским с помощью директивы "use client".
Серверные компоненты (Server Components)
- Рендерятся только на сервере.
- Могут обращаться к базе данных, файлам, секретным API — всему, что есть на сервере.
- Не могут использовать хуки состояния (useState, useEffect и т.п.).
- Не могут напрямую реагировать на действия пользователя.
Пример:
// app/about/page.tsx
export default function AboutPage() {
// Это серверный компонент по умолчанию
return <h1>О нас</h1>;
}
Клиентские компоненты (Client Components)
- Рендерятся на клиенте (в браузере).
- Могут использовать хуки состояния, эффекты, обработчики событий.
- Объявляются с помощью директивы "use client" в начале файла.
Пример:
// app/components/Counter.tsx
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Нажали {count} раз
</button>
);
}
Важно! Даже если вы импортируете клиентский компонент в серверный, Next.js "разрулит" это: сервер соберёт HTML, а клиентский компонент подключит интерактивность уже в браузере.
4. Схема работы App Router
Вот как это выглядит в виде схемы (упрощённо):
[Запрос пользователя]
|
v
[Next.js сервер]
|
| 1. Собирает дерево маршрутов по app/
| 2. Рендерит серверные компоненты (SSR)
| 3. Встраивает клиентские компоненты как "островки"
v
[Готовый HTML + JS]
|
v
[Браузер пользователя]
|
| 4. Показывает HTML сразу
| 5. "Гидратирует" клиентские компоненты (оживляет их)
v
[Пользователь видит страницу, может взаимодействовать]
Как это выглядит в коде?
// app/page.tsx
import Counter from "./components/Counter";
export default function HomePage() {
return (
<main>
<h1>Главная страница</h1>
<Counter /> {/* Это клиентский компонент */}
</main>
);
}
Пояснение:
- Всё, кроме <Counter />, рендерится на сервере.
- <Counter /> — клиентский, поэтому в браузер прилетает его JS-код, и только он может быть интерактивным.
5. Полезные нюансы
Как App Router помогает вашему приложению?
- SEO и скорость
Серверный рендеринг означает, что поисковики сразу видят содержимое страницы — это отлично для SEO. Пользователь тоже сразу видит контент, даже если интернет медленный или устройство слабое. - Гибкость и масштабируемость
Вы сами решаете, что рендерить на сервере, а что на клиенте. Например, формы, счетчики, анимации — на клиенте, а статический текст, статьи, каталоги — на сервере. - Безопасность
Серверные компоненты могут обращаться к секретам, API-ключам, базе данных — и всё это не "утечёт" в браузер пользователя.
Как устроена "гидратация"
Гидратация — это процесс, когда Next.js берёт уже готовый HTML (полученный с сервера), "добавляет" к нему JS-код и оживляет клиентские компоненты. Пользователь видит страницу сразу, а интерактивность появляется чуть позже, когда браузер скачает нужный JS.
Зачем это нужно?
Чтобы соединить скорость SSR и интерактивность CSR. Это как если бы вы купили мебель уже собранной (SSR), а потом просто включили свет (CSR).
Как App Router решает, что отдавать серверу, а что клиенту?
- Если компонент не помечен как "use client", он серверный.
- Если компоненту нужен useState, useEffect, обработчики событий — он должен быть клиентским.
- Серверные компоненты могут импортировать клиентские, но не наоборот.
- Вся цепочка вверх от клиентского компонента становится клиентской (важно для архитектуры!).
Таблица: что где работает
| Фича/возможность | Server Component | Client Component |
|---|---|---|
| Доступ к базе данных | ✅ | ❌ |
| Обработка событий | ❌ | ✅ |
| React useState/useEffect | ❌ | ✅ |
| Секреты, API-ключи | ✅ | ❌ |
| Доступ к window/document | ❌ | ✅ |
| Импорт других компонентов | ✅ (любых) | ✅ (только клиентских и обычных) |
6. Пример: серверный и клиентский рендеринг вместе
Давайте соберём простой пример — страницу с приветствием и кнопкой-счётчиком.
1. Серверный компонент:
// app/page.tsx
import Counter from "./components/Counter";
export default function HomePage() {
return (
<main>
<h1>Добро пожаловать!</h1>
<Counter />
</main>
);
}
2. Клиентский компонент:
// app/components/Counter.tsx
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Кликнули {count} раз
</button>
);
}
Пояснение:
- HomePage рендерится на сервере.
- Counter рендерится на клиенте, может использовать состояние и обработчики событий.
7. Типичные ошибки при работе с App Router и рендерингом
Ошибка №1: попытка использовать хуки состояния или window/document в серверном компоненте.
Если вы напишете useState или обратитесь к window в компоненте без "use client", получите ошибку: сервер не знает, что такое браузерные объекты и хуки.
Ошибка №2: забыли добавить "use client" в начало файла клиента.
Без этой директивы компонент останется серверным, даже если вы используете хуки. Это вызовет ошибку компиляции.
Ошибка №3: импорт клиентского компонента в серверный, а потом использовать серверные фичи.
Если вы импортируете клиентский компонент в серверный — всё нормально. Но если наоборот — нельзя! Клиентский компонент не может импортировать серверный, иначе Next.js ругнётся.
Ошибка №4: неправильная организация файлов.
Если вы положите клиентский компонент не в папку components, а прямо в page.tsx и забудете "use client", он останется серверным.
Ошибка №5: попытка использовать секреты или серверные API в клиентском компоненте.
Если вы случайно попытаетесь получить API-ключ или доступ к базе в клиентском компоненте — Next.js не даст этого сделать (и слава богу!).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ