Урок 11

Хлебные крошки и навигация

Хлебные крошки (breadcrumbs) показывают путь пользователя в иерархии. Помогают ориентироваться и быстро вернуться на уровень выше.

useMatches

React Router v6.4+ предоставляет useMatches — массив совпавших маршрутов:

import { useMatches, Link } from 'react-router-dom';

function Breadcrumbs() {
    const matches = useMatches();

    return (
        <nav>
            {matches.map((match, i) => (
                <span key={match.pathname}>
                    {i > 0 && ' / '}
                    <Link to={match.pathname}>
                        {(match.handle as { crumb?: string })?.crumb ?? match.pathname}
                    </Link>
                </span>
            ))}
        </nav>
    );
}

handle в маршрутах

Маршрут может иметь handle с данными для крошек:

const router = createBrowserRouter([
    {
        path: '/',
        element: <Root />,
        handle: { crumb: 'Главная' },
        children: [
            {
                path: 'posts',
                element: <Posts />,
                handle: { crumb: 'Посты' },
                children: [
                    {
                        path: ':id',
                        element: <PostDetail />,
                        handle: { crumb: 'Пост' },
                    },
                ],
            },
        ],
    },
]);

useMatches вернёт массив с handle для каждого совпавшего маршрута. Крошки строятся автоматически.

Динамический crumb

Если название зависит от данных — используй функцию:

handle: {
    crumb: (data: { title: string }) => data.title,
}

Или передавай через loader.

Хлебные крошки на странице товара

Главная / Каталог / Электроника / Смартфоны / iPhone 15

Каждый уровень — ссылка.

Навигация назад

function BackButton() {
    const navigate = useNavigate();

    return <button onClick={() => navigate(-1)}>← Назад</button>;
}

Хлебные крошки через props

Если не используешь data router:

function Breadcrumbs({ items }: { items: { label: string; to?: string }[] }) {
    return (
        <nav>
            {items.map((item, i) => (
                <span key={i}>
                    {i > 0 && ' / '}
                    {item.to ? <Link to={item.to}>{item.label}</Link> : item.label}
                </span>
            ))}
        </nav>
    );
}

<Breadcrumbs
    items={[
        { label: 'Главная', to: '/' },
        { label: 'Посты', to: '/posts' },
        { label: post.title },
    ]}
/>

Стилизация

Схема:

<nav aria-label="breadcrumb">
    <ol className="breadcrumbs">
        {items.map((item, i) => (
            <li key={i}>
                {i < items.length - 1 ? (
                    <Link to={item.to}>{item.label}</Link>
                ) : (
                    <span aria-current="page">{item.label}</span>
                )}
            </li>
        ))}
    </ol>
</nav>

Доступность

  • <nav aria-label="breadcrumb">
  • <ol> для порядка
  • aria-current="page" на последнем элементе
  • Разделители — CSS ::after, не текст

Итоги

  • useMatches для автоматических крошек
  • handle.crumb в маршруте
  • Props для ручных крошек
  • navigate(-1) для кнопки назад
  • ARIA для доступности