Skip to content

Файлы роутинга

Как работать со страницами и другими файлами роутинга Next.js App Router.

Назначение

src/app/** — точка входа приложения и слой файлового роутинга Next.js.

Файлы роутинга не реализуют интерфейс. Они описывают маршрут: читают параметры, получают данные первого рендера, подготавливают кеш или состояние и передают результат в screen.

Границы слоя описаны в Архитектура → Слои → App.

Граница ответственности

ОбластьГде живёт
Файлы маршрутов (page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx)src/app/**
Параметры маршрута, metadata, redirect(), notFound()src/app/**
Серверные запросы для первого рендераsrc/app/**, через готовые клиенты и сервисы нижних слоёв
Прогрев SWR-кеша, начальное состояние, подключение провайдеровsrc/app/**, только через готовые обёртки из нижних слоёв
UI страницыscreens/
Каркас страницы: header, footer, sidebarlayouts/
Провайдеры, сторы, хуки, API-клиенты, сервисынижние слои (screens/, business/, infrastructure/, shared/)
CSS Modules и стили компонентоврядом с компонентами, не в src/app/**

Что можно делать в page.tsx

  • Экспортировать metadata или generateMetadata.
  • Читать params и searchParams.
  • Нормализовать и валидировать параметры маршрута.
  • Делать серверные запросы для первого рендера через готовые клиенты или сервисы.
  • Вызывать redirect() и notFound().
  • Готовить начальные данные для screen.
  • Готовить SWR fallback и передавать его в готовый провайдер.
  • Подключать готовый провайдер стора страницы и передавать начальное состояние.
  • Рендерить screen или композицию из готовых обёрток и screen.

Что запрещено

  • Писать UI-разметку страницы прямо в файле роутинга.
  • Создавать локальные компоненты внутри src/app/**.
  • Добавлять CSS Modules, стили компонентов, components/, styles/, hooks/, stores/, services/ внутри src/app/**.
  • Реализовывать провайдеры, сторы, хуки, API-клиенты или сервисы в файлах роутинга.
  • Размещать бизнес-логику, мапперы и правила предметной области в файлах роутинга.
  • Вызывать useSWR и доменные клиентские хуки в файлах роутинга.

Страницы

Страница объявляется через export default function. Для серверных запросов используется async function.

tsx
import type { Metadata } from 'next'
import { ProfileScreen } from 'screens/profile'

export const metadata: Metadata = {
  title: 'Профиль',
  description: 'Страница профиля пользователя',
}

type ProfilePageProps = {
  params: Promise<{ id: string }>
}

export default async function ProfilePage({ params }: ProfilePageProps) {
  const { id } = await params

  return <ProfileScreen id={id} />
}

Данные первого рендера

Если данные нужны до первого рендера, page.tsx получает их на сервере и передаёт в screen. Сам запрос выполняется через готовый клиент или сервис нижнего слоя.

tsx
import { notFound } from 'next/navigation'
import { userApi } from 'infrastructure/backend-api'
import { UserScreen } from 'screens/user'

type UserPageProps = {
  params: Promise<{ id: string }>
}

export default async function UserPage({ params }: UserPageProps) {
  const { id } = await params
  const user = await userApi.users.get(id)

  if (!user) {
    notFound()
  }

  return <UserScreen user={user} />
}

Если данные нужны нескольким клиентским SWR-хукам, файл роутинга может обернуть дерево в SWRConfig и передать fallback. Запросы стартуют на сервере, а клиентские хуки получают готовые данные из кеша.

Ключи fallback должны совпадать с ключами внутри готовых SWR-хуков.

tsx
import type { ReactNode } from 'react'
import { SWRConfig } from 'swr'
import { backendApi } from 'infrastructure/backend-api'

type FeedLayoutProps = {
  children: ReactNode
}

export default async function FeedLayout({ children }: FeedLayoutProps) {
  const userPromise = backendApi.user.getCurrent()
  const postsPromise = backendApi.posts.list()

  return (
    <SWRConfig
      value={{
        fallback: {
          '/api/user': userPromise,
          '/api/posts': postsPromise,
        },
      }}
    >
      {children}
    </SWRConfig>
  )
}

Подробнее о серверных запросах и SWR-кеше: REST → Серверные компоненты, REST → Клиентские компоненты.

Инициализация состояния

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

tsx
import { ProfileScreen, ProfileStoreProvider } from 'screens/profile'

type ProfilePageProps = {
  params: Promise<{ id: string }>
}

export default async function ProfilePage({ params }: ProfilePageProps) {
  const { id } = await params

  return (
    <ProfileStoreProvider initialState={{ userId: id }}>
      <ProfileScreen />
    </ProfileStoreProvider>
  )
}

Layout

layout.tsx подключает готовую инициализацию приложения: глобальные стили, провайдеры и верхнеуровневые обёртки из нижних слоёв.

Вёрстка layout-каркаса выносится в слой layouts/. Реализация провайдеров, стилей и UI не размещается в app/.

Error и Not Found

error.tsx и not-found.tsx делегируют разметку готовым screen или widget. В файле роутинга остаётся только адаптация API Next.js к пропсам нижнего слоя.

tsx
'use client'

import { ErrorScreen } from 'screens/error'

type ErrorPageProps = {
  error: Error & { digest?: string }
  reset: () => void
}

const ErrorPage = ({ error, reset }: ErrorPageProps) => {
  return <ErrorScreen error={error} reset={reset} />
}

export default ErrorPage