Роутинг

Каждая папка в app/ — это сегмент URL, а специальные файлы определяют, что показывать.


          app/
          ├── page.tsx           # → /
          ├── about/
          │   └── page.tsx       # → /about
          └── blog/
            └── [slug]/
            └── page.tsx   # → /blog/hello-world
          

ПОЛНЫЙ СПИСОК СПЕЦИАЛЬНЫХ ФАЙЛОВ

  • page.tsx - Страница (UI)
  • layout.tsx - Обертка для страниц, сохраняется при навигации
  • template.tsx - Как layout, но создается заново при навигации
  • loading.tsx - UI загрузки (Suspense)
  • error.tsx - UI ошибки (Error Boundary)
  • not-found.tsx - UI 404
  • route.ts - API эндпоинт
  • middleware.ts - Перехват запросов (в корне)

ТИПЫ МАРШРУТОВ

  • Статические маршруты
    
                  app/
                  ├── page.tsx           # /
                  ├── about/page.tsx     # /about
                  └── contact/page.tsx   # /contact
                  
  • Динамические маршруты
    
                  # Одиночный параметр
                  app/blog/[slug]/page.tsx          # /blog/hello-world
                  # params: { slug: 'hello-world' }
    
                  # Несколько параметров
                  app/category/[category]/[id]/page.tsx  # /category/phones/123
                  # params: { category: 'phones', id: '123' }
                  
  • Catch-all маршруты ([...slug])
    
                  app/docs/[...slug]/page.tsx
                  # /docs/getting-started/installation
                  # params: { slug: ['getting-started', 'installation'] }
                  
  • Optional catch-all ([[...slug]])
    
                  app/docs/[[...slug]]/page.tsx
                  # /docs → params.slug = undefined
                  # /docs/getting-started → params.slug = ['getting-started']
                  
  • Группы маршрутов ((folder))
    
                  app/(marketing)/page.tsx      # / (без /marketing)
                  app/(shop)/products/page.tsx  # /products (без /shop)
                  # Скобки НЕ влияют на URL, только на организацию
                  
  • Параллельные маршруты (@folder)
    
                  app/@auth/login/page.tsx      # Модалка логина
                  app/@dashboard/page.tsx       # Дашборд
                  # Рендерятся одновременно
                  
  • Перехватывающие маршруты ((..)folder)
    
                  app/(.)photos/[id]/page.tsx   # Перехватывает /photos/123
                  # Аналогично относительным путям в файловой системе
                  

НАВИГАЦИЯ

Компонент Link (предпочтительный способ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Link from 'next/link'; export default function Navigation() { return ( <nav> <Link href="/">Главная</Link> <Link href="/about">О нас</Link> <Link href="/products/123">Товар</Link> <Link href="/blog/post-1">Статья</Link> {/* С заменой истории */} <Link href="/dashboard" replace>Дашборд (без добавления в историю)</Link> {/* С prefetch (по умолчанию true в production) */} <Link href="/heavy-page" prefetch={false}>Тяжелая страница</Link> </nav> );}

Хук useRouter (для программной навигации)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'use client';import { useRouter } from 'next/navigation'; export default function NavigationButtons() { const router = useRouter(); return ( <div> <button onClick={() => router.push('/about')}>О нас</button> <button onClick={() => router.replace('/')}>На главную (без истории)</button> <button onClick={() => router.back()}>Назад</button> <button onClick={() => router.forward()}>Вперед</button> <button onClick={() => router.refresh()}>Обновить данные</button> <button onClick={() => router.prefetch('/heavy')}>Предзагрузить</button> </div> );}

ПОЛУЧЕНИЕ ПАРАМЕТРОВ

В серверном компоненте

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// app/products/[category]/[id]/page.tsxexport default async function ProductPage({ params, searchParams }: { params: { category: string; id: string }; searchParams: { [key: string]: string | string[] | undefined };}) { // params из динамического маршрута console.log(params.category); // 'phones' console.log(params.id); // '123' // searchParams из query-строки console.log(searchParams.sort); // 'price' console.log(searchParams.page); // '2' return <div>Product</div>;}

На клиенте

1
2
3
4
5
6
7
8
9
10
11
12
13
'use client';import { useParams, useSearchParams, usePathname } from 'next/navigation'; export default function ClientComponent() { const params = useParams(); // { category: 'phones', id: '123' } const searchParams = useSearchParams(); // URLSearchParams объект const pathname = usePathname(); // '/products/phones/123' const sort = searchParams.get('sort'); // 'price' const page = searchParams.get('page'); // '2' return <div>...</div>;}