1. Page Router: классика Next.js
Если вы только начали изучать Next.js, то вполне вероятно, что вы столкнулись с вопросом:
"Почему в одном проекте папка pages, а в другом — app? В чём разница? Какой способ лучше?"
Это не баг, а фича! Next.js исторически развивался с классическим Page Router (pages/). Со временем команда Next.js внедрила новый, более современный подход — App Router (app/).
Сейчас оба способа существуют параллельно, но App Router — будущее Next.js.
Концепция
Page Router — это старый-добрый способ организации маршрутов в Next.js, который появился ещё в первых версиях фреймворка.
Главная идея: каждый файл в папке pages/ становится отдельной страницей (роутом) вашего сайта.
Пример структуры:
/pages
├── index.js // Главная страница: /
├── about.js // Страница "О нас": /about
└── blog
└── [id].js // Динамическая страница: /blog/123
- Все файлы в папке pages/ автоматически становятся маршрутами.
- Файл pages/index.js — это корень сайта (/).
- Файл pages/about.js — страница /about.
- Файл с квадратными скобками ([id].js) — динамический маршрут: /blog/123, /blog/abc и т.д.
Как работает Page Router?
- Каждый компонент из pages/ — это React-компонент, который экспортируется по умолчанию (export default).
- Можно использовать функции getStaticProps, getServerSideProps, getInitialProps для загрузки данных.
- Для навигации используется компонент <Link href="/about"> и хук useRouter().
Пример кода: страница About
// pages/about.js
export default function About() {
return <h1>О компании</h1>;
}
Особенности Page Router
- Всё просто и прозрачно.
- Поддерживает статическую генерацию (SSG), серверный рендеринг (SSR), инкрементальную регенерацию (ISR).
- Хорошо подходит для небольших и средних проектов.
- Ограниченная гибкость в организации макетов (layouts) и вложенных шаблонов.
2. App Router: современный взгляд на маршрутизацию
App Router — это новая архитектура маршрутизации, введённая в Next.js 13 и активно развиваемая в Next.js 15.
Главная идея: вся маршрутизация строится на папке app/, где каждая папка — потенциальный маршрут, а специальные файлы (page.js, layout.js, loading.js, и др.) определяют структуру и поведение страницы.
Пример структуры:
/app
├── page.js // Главная страница: /
├── about
│ └── page.js // Страница "О нас": /about
└── blog
├── [id]
│ └── page.js // Динамический маршрут: /blog/123
└── layout.js // Макет для блога
Пояснение:
- Каждый подкаталог в app/ — это сегмент маршрута.
- Файл page.js (или page.tsx) — компонент страницы.
- Файл layout.js — компонент макета (layout), оборачивает вложенные страницы.
- Можно использовать loading.js для отображения состояния загрузки, error.js для ошибок, template.js для шаблонов и т.д.
- Поддержка Server Components (серверные компоненты), Client Components (клиентские), Server Actions и других современных фич.
Как работает App Router?
- Строит дерево маршрутов на основе структуры папок и файлов.
- Позволяет создавать вложенные макеты, параллельные маршруты, группировать страницы и компоненты.
- Поощряет разделение на Server и Client Components для оптимизации производительности.
- Для навигации используется тот же <Link />, но есть нюансы с состоянием, асинхронной загрузкой и т.п.
Пример кода: страница About
// app/about/page.js
export default function AboutPage() {
return <h1>О компании (App Router)</h1>;
}
3. Таблица сравнения: Page Router vs App Router
| Характеристика | Page Router (pages/) | App Router (app/) |
|---|---|---|
| Где живут маршруты | В папке |
В папке |
| Принцип маршрутизации | Файл = страница | Папка = сегмент, файл page.js = страница |
| Макеты (layouts) | Только через , слабая вложенность |
Любая вложенность через |
| Динамические маршруты | |
|
| Загрузка данных | , |
Любой async Server Component |
| Server/Client Components | Только Client Components | Server и Client Components |
| Поддержка Server Actions | Нет | Да |
| Гибкость шаблонов | Ограниченная | Максимальная (layout, template, error) |
| Статус | Поддерживается, но legacy | Новый стандарт, развивается |
4. Примеры: как выглядит одно и то же приложение
Пример 1. Главная страница и страница "О нас"
Page Router:
/pages
├── index.js // /
└── about.js // /about
// pages/index.js
export default function Home() {
return <h1>Главная</h1>;
}
// pages/about.js
export default function About() {
return <h1>О нас</h1>;
}
App Router:
/app
├── page.js // /
└── about
└── page.js // /about
// app/page.js
export default function HomePage() {
return <h1>Главная (App Router)</h1>;
}
// app/about/page.js
export default function AboutPage() {
return <h1>О нас (App Router)</h1>;
}
Пример 2. Динамический маршрут "Пост блога"
Page Router:
/pages
└── blog
└── [id].js // /blog/123
// pages/blog/[id].js
import { useRouter } from 'next/router';
export default function BlogPost() {
const router = useRouter();
const { id } = router.query;
return <h1>Пост блога: {id}</h1>;
}
App Router:
/app
└── blog
└── [id]
└── page.js // /blog/123
// app/blog/[id]/page.js
export default function BlogPost({ params }) {
return <h1>Пост блога: {params.id}</h1>;
}
Обратите внимание: В App Router параметры маршрута передаются через пропс params, а не через хук.
5. Ключевые концепции App Router
Layouts (Макеты) и вложенность
App Router позволяет создавать отдельные макеты для разных частей сайта. Например, у блога может быть свой layout, который будет оборачивать все страницы блога (и только их).
/app
├── layout.js // Общий макет для всего сайта
└── blog
├── layout.js // Макет только для блога
└── [id]
└── page.js
// app/layout.js
export default function RootLayout({ children }) {
return (
<html>
<body>
<header>Меню сайта</header>
<main>{children}</main>
<footer>Подвал</footer>
</body>
</html>
);
}
// app/blog/layout.js
export default function BlogLayout({ children }) {
return (
<section>
<nav>Меню блога</nav>
<div>{children}</div>
</section>
);
}
Страницы блога будут автоматически обёрнуты в оба макета: сначала RootLayout, потом BlogLayout.
Server и Client Components
App Router поощряет использование серверных компонентов по умолчанию (они не попадают в JS-бандл на клиент). Для интерактивности используется директива 'use client' в начале файла.
// app/counter/page.js
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Счётчик: {count}</button>;
}
Асинхронная загрузка данных
В App Router можно делать асинхронные компоненты (async function), сразу загружая данные на сервере:
// app/users/page.js
export default async function UsersPage() {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await res.json();
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
6. Когда использовать Page Router, а когда App Router?
Page Router (pages/):
- Если у вас старый проект, и вы не хотите его переписывать.
- Если не нужны новые фичи (Server Components, layouts, Server Actions).
- Если хочется стабильности и понятности.
App Router (app/):
- Если вы начинаете новый проект.
- Если нужны современные возможности Next.js.
- Если хочется максимальной гибкости, вложенных макетов, SSR/SSG/ISR "на стероидах".
- Если не боитесь учить новое (а это, судя по тому, что вы читаете эту лекцию, — про вас!).
Важно: В одном проекте можно использовать оба подхода, но это не рекомендуется для новых проектов.
7. Типичные ошибки и особенности перехода
Ошибка №1: Путаете синтаксис параметров.
В Page Router параметры маршрута (например, [id]) получаете через хук useRouter, а в App Router — через пропс params. Если перепутаете — получите undefined или ошибку.
Ошибка №2: Пытаетесь использовать getStaticProps/getServerSideProps в App Router.
В App Router эти функции больше не работают! Загрузка данных теперь делается через async Server Components.
Ошибка №3: Забываете про layout.js и page.js.
В App Router маршрут без файла page.js не считается страницей и не будет доступен по URL.
Ошибка №4: Не указываете 'use client' там, где нужен интерактивный код.
По умолчанию компоненты в App Router — серверные. Если используете useState, useEffect и т.п., обязательно добавьте 'use client' в начало файла.
Ошибка №5: Путаете вложенность макетов.
В App Router макеты могут быть вложенными, и порядок обёртывания важен. Не забывайте, что layout.js влияет на все вложенные сегменты.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ