App Router vs Pages Router в Next.js — сравнение и выбор

27 июля 2026
Автор

Антон Ларичев

Введение

Next.js поддерживает две системы маршрутизации: старый Pages Router (существует с версии 1.0) и новый App Router, представленный в версии 13 и ставший стабильным в 13.4. Обе системы работают в одном проекте, что позволяет мигрировать постепенно.

Разобраться в отличиях важно, чтобы принять правильное решение для нового проекта и понять, стоит ли мигрировать существующий.

Курсы PurpleSchool

Учитесь у практикующих разработчиков — без воды

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Структура папок

Pages Router использует папку pages/:

pages/
├── index.tsx           # /
├── about.tsx           # /about
├── blog/
│   ├── index.tsx       # /blog
│   └── [slug].tsx      # /blog/:slug
└── api/
    └── users.ts        # /api/users

App Router использует папку app/:

app/
├── layout.tsx          # корневой layout (обязателен)
├── page.tsx            # /
├── about/
│   └── page.tsx        # /about
├── blog/
│   ├── page.tsx        # /blog
│   └── [slug]/
│       └── page.tsx    # /blog/:slug
└── api/
    └── users/
        └── route.ts    # /api/users (Route Handlers)

В App Router каждый маршрут — это папка с файлом page.tsx. Это добавляет гибкость: рядом с page.tsx можно положить layout.tsx, loading.tsx, error.tsx, not-found.tsx — и они автоматически привяжутся к маршруту.

Рендеринг и серверные компоненты

Pages Router: все компоненты — клиентские по умолчанию. Серверная логика только в getServerSideProps, getStaticProps, getInitialProps.

App Router: все компоненты — серверные по умолчанию (React Server Components). Для клиентского кода нужна директива 'use client':

// App Router — серверный компонент (по умолчанию)
// Может напрямую делать fetch, обращаться к БД
async function UserProfile({ userId }: { userId: string }) {
  const user = await fetchUser(userId); // прямо в компоненте!
  return <div>{user.name}</div>;
}

// App Router — клиентский компонент
'use client';

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

В Pages Router такого разделения нет — все компоненты страниц могут использовать хуки.

Получение данных

Pages Router

// pages/blog/[slug].tsx
import type { GetStaticProps, GetStaticPaths } from 'next';

interface Props {
  post: Post;
}

export default function BlogPost({ post }: Props) {
  return <article>{post.content}</article>;
}

export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await fetchAllPosts();
  return {
    paths: posts.map(p => ({ params: { slug: p.slug } })),
    fallback: 'blocking',
  };
};

export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
  const post = await fetchPost(params!.slug as string);
  return { props: { post }, revalidate: 60 };
};

App Router

// app/blog/[slug]/page.tsx
interface Props {
  params: { slug: string };
}

// Данные получают прямо в компоненте
export default async function BlogPost({ params }: Props) {
  const post = await fetchPost(params.slug); // async компонент
  return <article>{post.content}</article>;
}

// Генерация статических путей
export async function generateStaticParams() {
  const posts = await fetchAllPosts();
  return posts.map(p => ({ slug: p.slug }));
}

App Router убирает специальные функции (getStaticProps и т.д.) — данные получаются прямо в async-компонентах.

Layouts — вложенные макеты

Pages Router: общие макеты реализуются вручную через _app.tsx или оборачиванием компонентов.

App Router: вложенные layouts встроены в систему маршрутизации:

// app/layout.tsx — применяется ко всему сайту
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ru">
      <body>
        <Header />
        {children}
        <Footer />
      </body>
    </html>
  );
}

// app/dashboard/layout.tsx — применяется только к /dashboard/*
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="dashboard">
      <Sidebar />
      <main>{children}</main>
    </div>
  );
}

Layouts сохраняют своё состояние при навигации между страницами одного уровня — это важное преимущество для производительности.

Метаданные

Pages Router: метаданные через компонент <Head> из next/head:

import Head from 'next/head';

export default function Page() {
  return (
    <>
      <Head>
        <title>Заголовок страницы</title>
        <meta name="description" content="Описание" />
      </Head>
      <main>...</main>
    </>
  );
}

App Router: Metadata API через экспорт объекта metadata или функции generateMetadata:

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await fetchPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { images: [post.coverImage] },
  };
}

Сравнительная таблица

Параметр Pages Router App Router
Папка pages/ app/
Компоненты Клиентские Серверные по умолчанию
Получение данных getServerSideProps / getStaticProps async-компоненты + fetch
Layouts Ручная реализация Встроенные вложенные layouts
Метаданные <Head> из next/head Metadata API
Loading-состояния Ручная реализация loading.tsx
Error Boundaries Ручная реализация error.tsx
Стримминг / Suspense Ограниченная поддержка Полная поддержка
Стабильность Стабильный (устаревающий) Стабильный (рекомендуемый)

Что выбрать для нового проекта

Новый проект → App Router. Это официально рекомендованный подход. Он даёт Server Components, встроенные layouts, стримминг и лучший SEO через Metadata API. Экосистема библиотек активно адаптируется под App Router.

Существующий Pages Router проект — можно не мигрировать немедленно. Pages Router поддерживается и не планируется к удалению. Миграция имеет смысл, если нужны специфические возможности App Router.

Постепенная миграция возможна: в одном проекте могут сосуществовать обе папки (pages/ и app/). Маршруты из app/ имеют приоритет.

Итог

App Router — это эволюция Next.js, которая меняет фундаментальный подход к рендерингу: от клиентских компонентов с серверными функциями к серверным компонентам по умолчанию. Это требует нового мышления, но даёт лучшую производительность и более чистую архитектуру.

Для углублённого изучения — смотрите курс Next.js на PurpleSchool.

Стрелочка влевоMetadata API в Next.js 13+ — SEO-метаданные в App RouterApp Router в Next.js — как работает новая маршрутизацияСтрелочка вправо

Все гайды по Next-js

Открыть базу знаний

Отправить комментарий