Урок 04

Навигация и Link

Link

Основной способ навигации:

import Link from 'next/link';

<Link href="/about">О нас</Link>
<Link href={`/posts/${post.id}`}>{post.title}</Link>

Преимущества

  • Клиентская навигация — без перезагрузки
  • Предзагрузка — Next.js заранее загружает страницу при наведении
  • Scroll restoration — сохраняет позицию
  • Prefetch — только для видимых ссылок в viewport

Prefetch

По умолчанию включён. Отключить:

<Link href="/heavy" prefetch={false}>Тяжёлая страница</Link>

replace

Не добавляет запись в историю:

<Link href="/login" replace>Войти</Link>

scroll

Не скроллить к началу:

<Link href="/posts" scroll={false}>Посты</Link>

useRouter

Программная навигация:

'use client';

import { useRouter } from 'next/navigation';

export default function Button() {
    const router = useRouter();

    return (
        <>
            <button onClick={() => router.push('/about')}>О нас</button>
            <button onClick={() => router.replace('/login')}>Войти</button>
            <button onClick={() => router.back()}>Назад</button>
            <button onClick={() => router.forward()}>Вперёд</button>
            <button onClick={() => router.refresh()}>Обновить</button>
        </>
    );
}
  • push — новая запись в истории
  • replace — заменить текущую
  • back, forward — по истории
  • refresh — перезагрузить данные серверных компонентов

usePathname

Текущий путь:

'use client';

import { usePathname } from 'next/navigation';

export function Nav() {
    const pathname = usePathname();

    return (
        <nav>
            <Link href="/" className={pathname === '/' ? 'active' : ''}>Главная</Link>
            <Link href="/about" className={pathname === '/about' ? 'active' : ''}>О нас</Link>
        </nav>
    );
}

Активная ссылка

Компонент:

'use client';

import Link from 'next/link';
import { usePathname } from 'next/navigation';

export function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
    const pathname = usePathname();
    const isActive = pathname === href || pathname.startsWith(href + '/');

    return (
        <Link href={href} className={isActive ? 'active' : ''}>
            {children}
        </Link>
    );
}

redirect

В серверных компонентах и Server Actions:

import { redirect } from 'next/navigation';

export default async function Page() {
    const user = await getUser();
    if (!user) redirect('/login');

    return <h1>Профиль</h1>;
}

redirect бросает исключение — код после не выполняется.

redirect vs router.push

  • redirect — сервер, для условных редиректов
  • router.push — клиент, для обработчиков

notFound

import { notFound } from 'next/navigation';

if (!post) notFound();

useSelectedLayoutSegment

Для активных табов:

'use client';

import { useSelectedLayoutSegment } from 'next/navigation';

export function Tabs() {
    const segment = useSelectedLayoutSegment();

    return (
        <nav>
            <Link href="/dashboard/settings" className={segment === 'settings' ? 'active' : ''}>Настройки</Link>
            <Link href="/dashboard/profile" className={segment === 'profile' ? 'active' : ''}>Профиль</Link>
        </nav>
    );
}

Возвращает активный сегмент под текущим layout.

Программная навигация с query

'use client';

import { useRouter, usePathname, useSearchParams } from 'next/navigation';

const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();

const params = new URLSearchParams(searchParams);
params.set('page', '2');
router.push(`${pathname}?${params}`);

Итоги

  • Link — основной способ
  • Prefetch включён по умолчанию
  • useRouter для программной навигации
  • usePathname для активных ссылок
  • redirect в серверных компонентах
  • useSelectedLayoutSegment для табов