Breadcrumb Navigation with Schema Markup
An ordered list of links, a few lines of CSS and matching BreadcrumbList structured data. Accessible, responsive, and readable by Google, Baidu and screen readers — no JavaScript involved.
- JavaScript
- 0 KB
- Built with
<ol>,aria-current, JSON-LD- Browsers
- All current browsers (Chrome, Edge, Firefox, Safari)
- Updated
Wrap an ordered list (<ol>) of links in <nav aria-label="Breadcrumb">, mark the current page with aria-current="page", and add BreadcrumbList structured data with exactly the same items. Draw the separators with CSS so screen readers don't read them out.
Live demo: the same breadcrumb, two widths
This is the real breadcrumb of this page — the same trail you see at the top. In a wide space it shows the full path and shortens the current page title if needed. In a narrow space, like a phone, it becomes a single “back to parent” link.
Phone width
Desktop width — same HTML, same CSS
How the breadcrumb works
- An ordered list, because order matters
<ol>tells browsers and screen readers that the items form a sequence — “list, 3 items”. A row of<div>s or links separated by text says nothing about order. - A labelled navigation landmark
<nav aria-label="Breadcrumb">lets screen-reader users jump straight to the trail, and tells it apart from the main menu. aria-current="page"on the last itemMarks where the visitor is. The CSS uses the same attribute to style it as plain text — no extra class needed.
- Separators that screen readers skip
A “›” typed into the HTML is read out on every step. Drawn with
::beforeit is still read by many screen readers — unless you give it empty alternative text:content: "›" / "". - Long titles shorten themselves
Only the last item may shrink.
min-width: 0plustext-overflow: ellipsiscuts a long page title with “…” instead of breaking the layout. - A back link on small screens
A container query hides all items except the parent and turns its separator into “←”. On a phone, “one level up” is the useful part of a breadcrumb.
Copy the HTML, CSS and structured data
Exactly the code running in the demo. All class names start with ui-. The JSON-LD below is the breadcrumb markup this page uses in its own <head> — generated from the same list of items as the visible trail, so the two can never disagree.
<nav class="ui-crumbs" aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/lab/css-first-ui/">CSS-First UI Patterns</a></li>
<li><a href="/lab/css-first-ui/breadcrumb-navigation-schema/" aria-current="page">Breadcrumb Navigation with Schema Markup</a></li>
</ol>
</nav>/* Breadcrumb navigation — no JavaScript.
Full trail in wide spaces; in narrow spaces (under 30rem)
only a "back to parent" link remains. */
.ui-crumbs {
--ui-text: #1b1f3b;
--ui-link: #4f46e5;
--ui-sep: #9aa0bd;
container-type: inline-size;
font: 500 0.9375rem/1.4 system-ui, -apple-system, "Segoe UI", sans-serif;
}
.ui-crumbs ol {
display: flex;
align-items: center;
margin: 0;
padding: 0;
list-style: none;
}
.ui-crumbs li {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
}
/* Separator: drawn by CSS, silent for screen readers.
The second "content" line sets empty alternative text;
browsers that don't understand it keep the first line. */
.ui-crumbs li + li::before {
content: "›";
content: "›" / "";
margin: 0 0.6rem;
color: var(--ui-sep);
}
.ui-crumbs a {
color: var(--ui-link);
text-decoration: none;
}
.ui-crumbs a:hover {
text-decoration: underline;
}
.ui-crumbs a:focus-visible {
outline: 2px solid var(--ui-link);
outline-offset: 2px;
border-radius: 3px;
}
/* Current page: last item, plain text colour, shortened if too long */
.ui-crumbs li:last-child {
flex-shrink: 1;
min-width: 0;
}
.ui-crumbs a[aria-current="page"] {
overflow: hidden;
text-overflow: ellipsis;
color: var(--ui-text);
font-weight: 600;
}
.ui-crumbs a[aria-current="page"]:hover {
text-decoration: none;
}
/* Narrow space: only "← Parent" */
@container (max-width: 30rem) {
.ui-crumbs li {
display: none;
}
.ui-crumbs li:nth-last-child(2) {
display: flex;
}
.ui-crumbs li:nth-last-child(2)::before {
content: "←";
content: "←" / "";
margin: 0 0.4rem 0 0;
}
}<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://www.jademond.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "CSS-First UI Patterns",
"item": "https://www.jademond.com/lab/css-first-ui/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Breadcrumb Navigation with Schema Markup",
"item": "https://www.jademond.com/lab/css-first-ui/breadcrumb-navigation-schema/"
}
]
}
</script>Use absolute URLs in the structured data. Generate the visible trail and the JSON-LD from the same data in your CMS or template — a mismatch between the two is the most common breadcrumb schema error.
Breadcrumbs and SEO: Google and Baidu
Internal links to every parent page. Each breadcrumb links a page to all of its parents, with short, descriptive anchor text. That gives category pages a steady stream of internal links and gives crawlers a clear path up the hierarchy — on Google, Baidu and every other search engine, because it is plain HTML.
Breadcrumbs in Google's results. With BreadcrumbList structured data, Google can show your site's path instead of the raw URL. Since January 2025 Google shows this only on desktop; on phones it displays just the domain (Google Search Central announcement). The markup is still supported and still worth adding.
Baidu. Baidu reads the links, not the schema: a breadcrumb in the HTML is a crawlable path from deep pages up to your categories. Breadcrumbs that are inserted by JavaScript after the page loads give Baidu nothing to follow.
Readable URLs help too. When the path in your URLs matches the breadcrumb — /products/pumps/ under “Products › Pumps” — search engines and visitors see the same structure twice. For Chinese pages, Pinyin slugs keep those URLs readable.
Accessibility and browser support
| Situation | Without JS | Details |
|---|---|---|
| Screen readers | Works | Announced as “Breadcrumb, navigation”, a list of 3 items, with the current page marked. |
| Separators | Silent | Empty alternative text on content works in current Chrome, Edge, Safari and Firefox. Older browsers use the first content line, and some screen readers then read the “›”. |
| Keyboard | Works | Normal links, with a visible focus ring. |
| Phones | Shortened | Only the parent link is visible and announced. The full trail stays in the HTML for crawlers. |
| Older browsers | Degrades | Without container queries the full trail shows on every screen, shortened with “…”. |
If your CMS or framework renders breadcrumbs with JavaScript, move them to the server-side template. They are only useful to crawlers if they are in the HTML.
Questions about breadcrumb navigation
Should the current page be a link?
Either works. This demo follows the W3C pattern: a link with aria-current="page", styled as plain text. A plain <span> is fine too. In the structured data, the last item may also leave out its URL.
JSON-LD or microdata for breadcrumb schema?
Google supports both. JSON-LD is easier to maintain because it sits in one block instead of being spread over your HTML attributes. What matters is that it lists the same items as the visible breadcrumb.
Why don't I see breadcrumbs in Google's mobile results?
Since January 2025 Google shows only the domain on mobile. On desktop the breadcrumb path is still shown, and the structured data is still supported.
Where should the breadcrumb go on the page?
Above the main heading, below the site header — where visitors expect it. In the HTML it should come before the main content, as on this page.
Should a page have more than one breadcrumb trail?
Usually not. If a product sits in two categories, show the main path. Google can read several trails in the structured data, but visitors only need one.
This FAQ is itself built without JavaScript — see the accordion pattern.
Do search engines understand how your Chinese site is organised?
We plan category structures, URLs, breadcrumbs and internal links for Chinese websites — so Baidu finds and ranks the pages that matter. Then we run Baidu SEO and Chinese GEO for Western companies.
CSS-First UI Patterns are written and maintained by Jademond Digital, a Chinese digital marketing agency founded in Guangzhou in 2012. We build and audit websites for Baidu, Google and AI search — which is why we care what works without JavaScript. About us →