JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Динамические сегменты: [id], [...slug], [[...slug]] в Nex...

Динамические сегменты: [id], [...slug], [[...slug]] в Next.js 15 App Router

Модуль 4: Node.js, Next.js и Angular
8 уровень , 2 лекция
Открыта

1. Синтаксис динамических сегментов: [id]

В реальных приложениях редко бывает достаточно только статических маршрутов вроде /about или /contacts. Обычно нужно что-то вроде:

  • /users/123
  • /blog/react-hooks
  • /products/iphone-15

Где 123, react-hooks или iphone-15 — это переменные части адреса, которые определяют, какую именно информацию показывать.

В Next.js такие переменные части называются динамическими сегментами. Они позволяют создавать одну страницу, которая работает с разными параметрами из URL.

Основной синтаксис

Чтобы создать динамический сегмент, нужно назвать папку или файл в папке app/ в квадратных скобках. Например:


app/
  users/
    [id]/
      page.tsx

Пояснение: [id] — это имя параметра. Оно будет доступно в компоненте страницы.

Пример:

  • URL /users/42 отобразит компонент из app/users/[id]/page.tsx, где id = "42"
  • URL /users/aliceid = "alice"

Пример кода


// app/users/[id]/page.tsx

import React from "react";

type Props = {
  params: { id: string }
};

export default function UserPage({ params }: Props) {
  return (
    <div>
      <h1>Профиль пользователя</h1>
      <p>ID: {params.id}</p>
    </div>
  );
}

Пояснение:

  • Параметр id берётся из URL и доступен через пропс params.
  • Если открыть /users/123, на странице будет написано ID: 123.

Как это работает?

Next.js автоматически сопоставляет любую часть пути на месте [id] и передаёт её значение в компонент страницы.

2. Множественные динамические сегменты

Можно делать вложенные динамические сегменты. Например, для адреса /users/42/posts/99:


app/
  users/
    [userId]/
      posts/
        [postId]/
          page.tsx

В компоненте будут доступны оба параметра: userId и postId.


// app/users/[userId]/posts/[postId]/page.tsx

export default function PostPage({ params }) {
  return (
    <div>
      <h1>Пост пользователя</h1>
      <p>User ID: {params.userId}</p>
      <p>Post ID: {params.postId}</p>
    </div>
  );
}

3. Кастомизация URL: [...slug] — "catch-all" сегменты

Иногда нужно обработать адреса с произвольным количеством частей. Например:

  • /docs/intro
  • /docs/guides/getting-started
  • /docs/guides/advanced/optimizations

Вместо создания кучи вложенных папок можно использовать catch-all сегмент — многоточие в квадратных скобках: [...slug].


app/
  docs/
    [...slug]/
      page.tsx

Такой сегмент "собирает" все части пути после /docs/ в массив params.slug.

Пример

  • /docs/introparams.slug = ["intro"]
  • /docs/guides/getting-startedparams.slug = ["guides", "getting-started"]
  • /docs/guides/advanced/optimizationsparams.slug = ["guides", "advanced", "optimizations"]

// app/docs/[...slug]/page.tsx

import React from "react";

type Props = {
  params: { slug?: string[] }
};

export default function DocsPage({ params }: Props) {
  return (
    <div>
      <h1>Документация</h1>
      <p>Путь: {params.slug ? params.slug.join(" / ") : "Главная документации"}</p>
    </div>
  );
}

Если открыть /docs/guides/advanced/optimizations, увидите: Путь: guides / advanced / optimizations

Важно!

Если пользователь зайдёт просто на /docs, то params.slug будет undefined. Это не ошибка — просто массив пустой.

4. Опциональные catch-all сегменты: [[...slug]]

Что делать, если вы хотите, чтобы страница работала и для /docs (без дополнительных частей), и для /docs/что-угодно? Для этого есть опциональный catch-all сегмент: двойные квадратные скобки.


app/
  docs/
    [[...slug]]/
      page.tsx

Пояснение:

  • Теперь и /docs и /docs/anything/else будут попадать в этот маршрут.
  • Если путь — просто /docs, то params.slug будет undefined или пустым массивом.

Пример:


// app/docs/[[...slug]]/page.tsx

export default function DocsPage({ params }) {
  const slug = params.slug ?? [];
  return (
    <div>
      <h1>Документация</h1>
      <p>
        {slug.length === 0
          ? "Главная документации"
          : "Путь: " + slug.join(" / ")}
      </p>
    </div>
  );
}

5. Полезные нюансы

Сравнение: [id], [...slug], [[...slug]]

Синтаксис Что делает? Пример URL Значение params
[id]
Одна часть пути (обязательная)
/users/42
{ id: "42" }
[...slug]
Одна или больше частей (catch-all, обязательно хотя бы 1 часть)
/docs/a/b/c
{ slug: ["a","b","c"] }
[[...slug]]
Любое количество частей (в том числе ни одной, то есть опционально)
/docs или /docs/a/b
{ slug: undefined }
или
{ slug: ["a","b"] }

Как использовать параметры в компоненте

В Next.js App Router параметры из динамических сегментов попадают в проп params вашего компонента страницы. Обычно типизация выглядит так:


type Props = {
  params: { [key: string]: string | string[] | undefined }
}

Пояснение:

  • Для [id] — это строка.
  • Для [...slug] или [[...slug]] — это массив строк (или undefined).

6. Практика: динамические страницы в вашем учебном приложении

Давайте добавим к вашему учебному приложению страницу просмотра отдельной задачи по её id.

Структура:


app/
  tasks/
    [id]/
      page.tsx

Код:


// app/tasks/[id]/page.tsx

import React from "react";

// Имитация базы задач
const tasks = [
  { id: "1", title: "Изучить Next.js", done: false },
  { id: "2", title: "Сделать домашку", done: true },
];

export default function TaskPage({ params }) {
  const task = tasks.find((t) => t.id === params.id);

  if (!task) {
    return <div>Задача не найдена</div>;
  }

  return (
    <div>
      <h1>Задача: {task.title}</h1>
      <p>Статус: {task.done ? "Выполнено" : "В процессе"}</p>
    </div>
  );
}

Пояснение:

  • Откройте /tasks/1 — увидите первую задачу.
  • Откройте /tasks/999 — получите сообщение "Задача не найдена".

7. Типичные ошибки и нюансы

Ошибка №1: Папка названа без скобок
Если вы назовёте папку просто id вместо [id], динамическая маршрутизация работать не будет. Next.js будет считать это обычным статическим маршрутом.

Ошибка №2: Несоответствие типов
Для [id] параметр всегда строка, для [...slug] — массив строк. Если не учитывать это, можно получить ошибку при попытке вызвать .join() на строке или обратиться к индексу массива, когда его нет.

Ошибка №3: Необработанные значения params
Если не проверить, что params.slug существует и является массивом (для catch-all), можно получить ошибку при попытке использовать .join() на undefined. Всегда делайте проверку или используйте оператор ?? [].

Ошибка №4: Конфликт путей
Если у вас есть и статическая страница (/docs/page.tsx) и catch-all (/docs/[...slug]/page.tsx), Next.js может выбрать не тот маршрут. Следите за уникальностью путей.

Ошибка №5: Путаница между [...slug] и [[...slug]]
Если вы хотите, чтобы маршрут работал и без дополнительных сегментов (например, /docs), используйте двойные скобки — [[...slug]]. Обычные [...slug] не сработают для пустого пути.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ