Пошук уроків, статей та іншого контенту
Створите навігаційні ланцюжки Breadcrumbs і додасте їхнє семантичне представлення для користувачів та пошуковиків.
Breadcrumbs — це навігаційний ланцюжок, який показує поточне розташування сторінки в ієрархії сайту.
Наприклад:
Головна → Каталог → Смартфони → Pixel 9
Такий компонент допомагає:
користувачеві зрозуміти, де він перебуває;
швидко перейти на батьківський рівень;
допоміжним технологіям озвучити структуру сторінки;
пошуковим системам зрозуміти ієрархію URL.
Breadcrumbs не замінюють основну навігацію сайту. Це додаткова навігація, пов’язана з поточним контекстом сторінки.
Для Breadcrumbs варто використовувати:
<nav> — навігаційний блок;
aria-label — зрозуміла назва навігації;
<ol> — впорядкований список, оскільки елементи мають послідовність;
<li> — окремі елементи ланцюжка;
aria-current="page" — позначення поточної сторінки.
Приклад структури:
<nav aria-label="Навігаційний ланцюжок">
<ol>
<li>
<a href="/">Головна</a>
</li>
<li>
<a href="/catalog">Каталог</a>
</li>
<li>
<span aria-current="page">Смартфони</span>
</li>
</ol>
</nav>Поточну сторінку зазвичай не роблять посиланням. Вона вже відкрита, тому посилання на неї не додає користі.
У Next.js з App Router компонент можна створити як серверний компонент. Для навігації використаємо Link з next/link.
Створимо файл components/Breadcrumbs.tsx:
import Link from "next/link";
type BreadcrumbItem = {
label: string;
href?: string;
};
type BreadcrumbsProps = {
items: BreadcrumbItem[];
};
const siteUrl =
process.env.NEXT_PUBLIC_SITE_URL ?? "https://example.com";
export default function Breadcrumbs({ items }: BreadcrumbsProps) {
const jsonLd = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, index) => ({
"@type": "ListItem",
position: index + 1,
name: item.label,
item: `${siteUrl}${item.href ?? ""}`,
})),
};
return (
<>
<nav aria-label="Навігаційний ланцюжок">
<ol className="breadcrumbs">
{items.map((item, index) => {
const isCurrent = index === items.length - 1;
return (
<li key={`${item.label}-${index}`} className="breadcrumbs__item">
{isCurrent || !item.href ? (
<span aria-current={isCurrent ? "page" : undefined}>
{item.label}
</span>
) : (
<Link href={item.href}>{item.label}</Link>
)}
{!isCurrent && (
<span aria-hidden="true" className="breadcrumbs__separator">
/
</span>
)}
</li>
);
})}
</ol>
</nav>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>
</>
);
}Компонент виконує дві окремі задачі:
Рендерить видимий навігаційний ланцюжок.
Додає структуровані дані для пошукових систем.
type BreadcrumbItem = {
label: string;
href?: string;
};Властивість href є необов’язковою. Для останнього елемента її можна не передавати:
const breadcrumbs = [
{ label: "Головна", href: "/" },
{ label: "Каталог", href: "/catalog" },
{ label: "Смартфони" },
];Останній елемент визначається за його індексом:
const isCurrent = index === items.length - 1;Для нього виводиться:
<span aria-current="page">Смартфони</span>А для попередніх елементів — посилання:
<Link href="/catalog">Каталог</Link>aria-current="page" повідомляє скринрідерам, що цей елемент відповідає поточній сторінці.
Для пошукових систем Breadcrumbs описуються типом Schema.org BreadcrumbList.
Його структура має вигляд:
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Головна",
"item": "https://example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Каталог",
"item": "https://example.com/catalog"
}
]
}Кожен елемент повинен містити:
@type: "ListItem";
position — позицію в ланцюжку, починаючи з 1;
name — назву сторінки;
item — абсолютну URL-адресу сторінки.
У компоненті ці дані створюються програмно:
const jsonLd = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, index) => ({
"@type": "ListItem",
position: index + 1,
name: item.label,
item: `${siteUrl}${item.href ?? ""}`,
})),
};Змінна siteUrl повинна містити справжній домен застосунку:
const siteUrl =
process.env.NEXT_PUBLIC_SITE_URL ?? "https://example.com";У production-оточенні можна встановити змінну середовища:
NEXT_PUBLIC_SITE_URL=https://my-site.comВажливо, щоб у структурованих даних використовувалися абсолютні URL-адреси, а не лише /catalog.
Наприклад, створимо сторінку app/catalog/phones/page.tsx:
import Breadcrumbs from "@/components/Breadcrumbs";
const breadcrumbs = [
{ label: "Головна", href: "/" },
{ label: "Каталог", href: "/catalog" },
{ label: "Смартфони" },
];
export default function PhonesPage() {
return (
<main>
<Breadcrumbs items={breadcrumbs} />
<h1>Смартфони</h1>
<p>Перегляньте доступні моделі смартфонів.</p>
</main>
);
}У браузері користувач побачить:
Головна / Каталог / СмартфониВодночас HTML міститиме семантичну навігацію та JSON-LD для пошукових систем.
Структура компонента не залежить від конкретного CSS. Наприклад, для CSS Modules можна додати стилі до Breadcrumbs.module.css:
.breadcrumbs {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.breadcrumbs__item {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.breadcrumbs__item a {
color: #2563eb;
text-decoration: none;
}
.breadcrumbs__item a:hover {
text-decoration: underline;
}
.breadcrumbs__separator {
color: #6b7280;
}Якщо ланцюжок довгий, flex-wrap: wrap дозволить йому переноситися на новий рядок замість горизонтального переповнення.
dangerouslySetInnerHTMLNext.js не має окремого JSX-синтаксису для виведення JSON-LD. Тому структуровані дані додаються як текст усередині:
<script type="application/ld+json">
...
</script>У React для цього використовується dangerouslySetInnerHTML:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>JSON.stringify перетворює JavaScript-об’єкт на коректний JSON. Заміна символу < зменшує ризик некоректної інтерпретації значень усередині HTML-документа.
Не слід формувати JSON-LD конкатенацією рядків:
// Погано: значення можуть зламати JSON або HTML
const jsonLd = `{
"@type": "BreadcrumbList",
"name": "${item.label}"
}`;Надійніше спочатку створити звичайний об’єкт, а потім серіалізувати його через JSON.stringify.
Для динамічних маршрутів елементи можна створювати на основі даних сторінки.
Наприклад, сторінка товару може мати такий ланцюжок:
import Breadcrumbs from "@/components/Breadcrumbs";
type ProductPageProps = {
params: Promise<{
slug: string;
}>;
};
export default async function ProductPage({
params,
}: ProductPageProps) {
const { slug } = await params;
const product = {
name: "Pixel 9",
slug,
};
const breadcrumbs = [
{ label: "Головна", href: "/" },
{ label: "Каталог", href: "/catalog" },
{ label: "Смартфони", href: "/catalog/phones" },
{ label: product.name },
];
return (
<main>
<Breadcrumbs items={breadcrumbs} />
<h1>{product.name}</h1>
<p>Детальна інформація про товар.</p>
</main>
);
}Назви категорій і товарів краще отримувати з того самого джерела даних, що й основний вміст сторінки. Це допомагає уникати ситуацій, коли заголовок сторінки та Breadcrumbs містять різні назви.
Breadcrumbs повинні відображати логічний шлях, а не випадковий набір посилань:
Головна → Каталог → Смартфони → Pixel 9Не варто додавати до ланцюжка сторінки, які не є частиною ієрархії поточного розділу.
Назви мають бути короткими, але інформативними:
Головна → Блог → Next.js → BreadcrumbsНе варто використовувати лише технічні значення URL або незрозумілі скорочення.
Назва в name структурованих даних повинна відповідати тексту, який бачить користувач. Не слід додавати в JSON-LD елементи, яких немає у видимому Breadcrumbs.
Для простої сторінки, яка не належить до конкретного розділу, Breadcrumbs можуть бути непотрібними. Компонент має відображати реальну структуру сайту, а не створювати її штучно.
JSON-LD допомагає пошуковим системам інтерпретувати сторінку, але не гарантує показ Breadcrumbs у результатах пошуку. Пошукова система сама вирішує, як відображати результат.
<div> замість навігації<div>
Головна / Каталог / Смартфони
</div>Такий текст може бути візуально зрозумілим, але він не має потрібної семантики для скринрідерів і пошукових систем.
Краще використовувати <nav>, <ol> і <li>.
aria-currentЯкщо поточний елемент не позначений, користувач допоміжної технології може не зрозуміти, який пункт відповідає відкритій сторінці.
<span aria-current="page">Смартфони</span>Останній елемент зазвичай не повинен бути посиланням:
<span aria-current="page">Смартфони</span>Замість:
<a href="/catalog/phones">Смартфони</a>Позиції починаються з 1, а не з 0:
position: index + 1Для JSON-LD краще використовувати:
https://example.com/catalogа не:
/catalogНе варто додавати тільки JSON-LD, приховуючи Breadcrumbs від користувача. Структуровані дані повинні описувати видимий і корисний вміст сторінки.
Breadcrumbs показують ієрархію поточної сторінки.
Для семантики використовуйте <nav>, <ol>, <li> та aria-current="page".
У Next.js зручно передавати елементи ланцюжка через типізований масив.
Для SEO додавайте JSON-LD типу BreadcrumbList.
У структурованих даних використовуйте позиції від 1 та абсолютні URL.
Поточну сторінку краще відображати як текст, а не як посилання.
Видимі Breadcrumbs і JSON-LD повинні описувати одну й ту саму ієрархію.