Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Динамические маршруты

У каждой статьи свой адрес: /posts/hello-nextjs, /posts/server-components. Создавать папку под каждую статью нельзя, поэтому используют динамический сегмент — папку с именем в квадратных скобках. Она подходит для любого значения в этой части адреса.

Данные блога

Сначала вынесем статьи в отдельный файл. Создайте lib/posts.ts в корне проекта:

// lib/posts.ts
export type Post = {
  slug: string;
  title: string;
  body: string;
};
 
const posts: Post[] = [
  { slug: 'hello-nextjs', title: 'Привет, Next.js', body: 'Первая статья блога.' },
  { slug: 'server-components', title: 'Серверные компоненты', body: 'Как они работают.' },
];
 
export async function getPosts(): Promise<Post[]> {
  return posts;
}
 
export async function getPost(slug: string): Promise<Post | undefined> {
  return posts.find((post) => post.slug === slug);
}

Функции асинхронные, хотя пока читают массив: позже в них появится запрос к базе данных, а код страниц не изменится. slug — короткое имя статьи для адреса.

Папка [slug]

Создайте папку app/posts/[slug] и в ней page.tsx:

// app/posts/[slug]/page.tsx
import { getPost } from '@/lib/posts';
 
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);
 
  if (!post) {
    return <p>Статья не найдена</p>;
  }
 
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  );
}

Откройте /posts/hello-nextjs — появится заголовок «Привет, Next.js». По адресу /posts/abc будет текст «Статья не найдена».

Как работает params

Next.js передаёт странице свойство params — объект с частями адреса. Имя ключа совпадает с именем папки: папка [slug] даёт params.slug. В Next.js 16 params — это промис, поэтому его получают через await, а компонент страницы объявляют async. Почему страница может быть асинхронной, объясняет урок о серверных компонентах.

Вместо ручного типа можно использовать готовый помощник PageProps<'/posts/[slug]'>: Next.js создаёт его сам при npm run dev и npm run build, импортировать его не нужно. В курсе тип пишется вручную, чтобы было видно, что внутри.

Список со ссылками

Страница списка теперь берёт данные из того же файла:

// app/posts/page.tsx
import Link from 'next/link';
import { getPosts } from '@/lib/posts';
 
export default async function PostsPage() {
  const posts = await getPosts();
 
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link href={`/posts/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  );
}

Ссылки со страницы /posts теперь ведут на настоящие статьи.

Другие виды сегментов

ПапкаПодходит дляЗначение
[slug]/posts/a'a'
[...slug]/docs/a/b['a', 'b']
[[...slug]]/docs и /docs/a/bundefined или массив

Частая ошибка

Читать params.slug без await, как в статьях о Next.js 14. В Next.js 15 это ещё работало с предупреждением, а в Next.js 16 синхронный доступ удалён: params.slug будет undefined, и страница не найдёт статью. Всегда пишите const { slug } = await params.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.