К содержанию

Маршруты и общая структура страниц

Связываем файлы с адресами и сохраняем общую оболочку страниц.

На этой странице

Для Next.js 16.3.8 (App Router) и React 19.3.0. Обновлено .

Маршрут связывает адрес с содержимым страницы. В App Router папка задает часть адреса, файл page.tsx – страницу. Обычный файл компонента сам по себе не создает адрес.

Файлы и адреса

Структура проекта
afisha/
├── app/
│   ├── layout.tsx           → общая оболочка
│   ├── page.tsx             → /
│   └── events/
│       ├── page.tsx         → /events
│       └── [id]/
│           └── page.tsx     → /events/1, /events/2, ...
└── lib/
    └── events.ts           → данные, не страница

[id] – динамическая часть маршрута. Одна реализация страницы обслуживает разные идентификаторы. В браузере нужно открыть /events/1, а не адрес с буквальными скобками.

Общие данные

Создайте папку lib и файл events.ts. Эта модель понадобится следующим статьям:

lib/events.ts
export type EventItem = { id: number; name: string; price: number };
 
export const events: EventItem[] = [
  { id: 1, name: "Кинопоказ", price: 300 },
  { id: 2, name: "Лекция", price: 0 },
];
 
export function getEvent(id: string) {
  return events.find((event) => String(event.id) === id);
}

Параметр id из адреса приходит как строка. Мы сравниваем её со строковой записью идентификатора, поэтому "1" находит первое мероприятие, а "unknown" не находит ни одного. find возвращает объект или undefined.

Если на главной остался фильтр из Next-01, удалите из app/page.tsx его локальный const events и добавьте import { events } from "@/lib/events". Теперь главная, каталог и отдельные мероприятия используют один источник. Изменение цены в lib/events.ts отразится во всех этих местах.

Создайте app/events/page.tsx:

app/events/page.tsx
import Link from "next/link";
import { events } from "@/lib/events";
 
export default function EventsPage() {
  return (
    <main>
      <h1>Афиша</h1>
      <ul>
        {events.map((event) => (
          <li key={event.id}>
            <Link href={`/events/${event.id}`}>{event.name}</Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

@/ – алиас, то есть сокращенный путь, настроенный установщиком. В нашем проекте он указывает на корневую папку. Link поддерживает переходы Next.js без полной перезагрузки документа. Для внешнего сайта обычно достаточно обычной ссылки a.

Получаем параметр страницы

Создайте файл в папке [id]:

app/events/[id]/page.tsx
import { notFound } from "next/navigation";
import { getEvent } from "@/lib/events";
 
export default async function EventPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const event = getEvent(id);
  if (!event) notFound();
 
  return (
    <main>
      <h1>{event.name}</h1>
      <p>{event.price} ₽</p>
    </main>
  );
}

В Next.js 16 params – Promise: сначала дождитесь его через await. Тип после двоеточия описывает ожидаемый объект параметров. notFound() завершает показ этой страницы как несуществующей; свой экран для неё добавим в статье о состояниях.

Общая оболочка

Замените app/layout.tsx, сохранив созданные страницы:

app/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";
import "./globals.css";
 
export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="ru">
      <body>
        <nav aria-label="Основная навигация">
          <Link href="/">Главная</Link> <Link href="/events">Афиша</Link>
        </nav>
        {children}
      </body>
    </html>
  );
}

В children Next.js передает содержимое текущей страницы. Корневой layout содержит html и body. Вложенный app/events/layout.tsx мог бы добавить общую оболочку только каталогу и его дочерним страницам; второй html внутри него не нужен.

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

Попробуйте: откройте /events/1 напрямую, перейдите к лекции и откройте /events/99. Проверьте, что общая навигация не скопирована в каждую страницу.

Далее – сервер и браузер. Документация: страницы и общая структура, Link.