0% прочитано

Laravel BreadcrumbList JSON-LD in a Real Blog

In ICanUp, visible breadcrumbs and BreadcrumbList JSON-LD describe the same page hierarchy. I show how I connect navigation with structured data, generate sequential positions, use localized canonical URLs, and avoid creating a separate SEO hierarchy on top of application breadcrumbs.

10 вересня 2026 р. 8 хв читанняLaravel

When breadcrumbs appeared in ICanUp, I could have built another hierarchy specifically for JSON-LD.

That would eventually allow visible navigation and structured data to describe different paths.

For me, BreadcrumbList is a structured representation of the same breadcrumb hierarchy the user already sees.

My main rule is simple: visible breadcrumbs and BreadcrumbList should not have separate sources of truth. The SEO schema serializes the real public navigation hierarchy.
Laravel visible breadcrumbs converted into BreadcrumbList JSON-LD with localized canonical URLs
Visible breadcrumbs use the same public URL contract and become BreadcrumbList structured data without a separate SEO hierarchy.

BreadcrumbList does more than repeat breadcrumb labels.

It describes the ordered path of a page inside the site hierarchy through a list of ListItem objects.

Each item has a position, a name, and a public URL when the element is navigable.

JSON - Basic BreadcrumbList
{  "@context": "https://schema.org",  "@type": "BreadcrumbList",  "itemListElement": [    {      "@type": "ListItem",      "position": 1,      "name": "Blog",      "item": "https://icanup.com.ua/en"    },    {      "@type": "ListItem",      "position": 2,      "name": "Laravel",      "item": "https://icanup.com.ua/en/categories/laravel"    },    {      "@type": "ListItem",      "position": 3,      "name": "Current post"    }  ]}

The Visible Hierarchy Comes First

Breadcrumbs first need to make sense to a person.

They show where the current page lives relative to the Blog, Category, or other parent pages.

JSON-LD should not invent a parent that is missing from visible navigation.

Page type

Visible path

Category

Blog > Laravel

Post

Blog > Laravel > Current post

Static page

Blog > About

A Post Includes Its Category in the Hierarchy

For a technical post, the category is an important piece of context.

The Post breadcrumb can therefore contain not only Blog and title, but also the category between them.

This gives ICanUp a hierarchy that is useful for both navigation and structured data.

Post breadcrumb hierarchy
Blog  |  +-- Laravel        |        +-- Laravel BreadcrumbList JSON-LD in a Real Blog

Position Comes from Order, Not a Database ID

In Schema.org, every ListItem receives a sequential position.

This is the position inside the breadcrumb path, not the primary key of a category, menu item, or another database entity.

The first item is position 1, the second is 2, and the third is 3 regardless of their database IDs.

PHP - Sequential breadcrumb positions
$items = []; foreach ($breadcrumbs as $index => $breadcrumb) {    $items[] = [        '@type' => 'ListItem',        'position' => $index + 1,        'name' => $breadcrumb['label'],    ];}

The Current Page Does Not Always Need an item URL

In visible breadcrumbs, the current page is normally the last item and is not clickable.

I do not want to create fake navigation behavior only for the schema.

For a non-navigable element, name and position can remain while item is omitted.

JSON - Current non-navigable breadcrumb item
{  "@type": "ListItem",  "position": 3,  "name": "Current post"}
I do not mechanically add an item URL to every ListItem. The schema should preserve the navigation semantics of the visible breadcrumb.

Localized URLs Should Match Canonical Routes

A multilingual blog adds another constraint: breadcrumb URLs depend on locale.

The default UK locale has no locale prefix, while EN uses /en.

BreadcrumbList should not own separate localization logic for URLs.

Localized breadcrumb URLs
UK:https://icanup.com.ua/categories/laravel EN:https://icanup.com.ua/en/categories/laravel

This is the same URL contract I use for canonical and hreflang in multilingual Laravel.

Structured data should not independently decide when a locale prefix belongs in a URL.

Route Generation Remains the Source of Truth

I have already dealt with the cost of duplicating localized routing logic.

Breadcrumb URLs, canonical, hreflang, and other SEO consumers should use application routing rather than manually concatenated strings.

One route contract reduces the risk that a visible link and JSON-LD item start pointing to different URLs.

I described that approach in more detail in my post about logical route names and localized URLs in Laravel.

Nested Categories Make BreadcrumbList More Interesting

When a category tree becomes deeper, the breadcrumb path can become deeper too.

Instead of one Laravel level, a parent category and child category may both appear.

In that case, structured data should preserve the real ordered ancestor chain, not only the current category.

Nested category breadcrumb
Blog  |  +-- Development        |        +-- Laravel              |              +-- Current post

Labels Need Localization Too

The URL is not the only localized part of a breadcrumb.

The visible category label or page title should also match the current locale.

Otherwise an EN page could have a correct EN URL but a UK breadcrumb name inside JSON-LD.

Part

UK page

EN page

Category label

Laravel

Laravel

Post title

UK localized title

EN localized title

Category URL

/categories/laravel

/en/categories/laravel

BreadcrumbList and BlogPosting Can Live on the Same Page

A Post page is not limited to a single JSON-LD type.

BlogPosting describes the Post entity while BreadcrumbList describes its position in the site hierarchy.

These schemas complement each other instead of competing for the role of the primary structured object.

JSON - Different structured data responsibilities
[  {    "@type": "BlogPosting",    "headline": "Localized post title"  },  {    "@type": "BreadcrumbList",    "itemListElement": []  }]

I Do Not Duplicate the Breadcrumb Hierarchy in the SEO Layer

The risky design would be to build visible breadcrumbs in the frontend and manually describe the hierarchy again inside an SEO service.

Changing the category tree or page structure would then require two implementations to stay synchronized.

Structured data is safer when it is generated from an already normalized breadcrumb collection.

What I Treat as the Breadcrumb Item Contract

Field

Purpose

label

Visible and schema name

url

Localized public URL for a navigable item

navigable

Whether the element should have an item URL

order

Position inside the hierarchy

Static Pages Have a Simpler Path

Not every page needs a category hierarchy.

A static page can have a short path containing Blog and the current page.

This is another reason not to hardcode a Post-specific hierarchy inside the BreadcrumbList builder.

Static page breadcrumb
Blog > About

JSON-LD Needs to Be in the Initial HTML

BreadcrumbList is SEO structured data, so I verify more than the state after browser hydration.

The schema should be present in the page source returned to the crawler.

This follows the same SSR principle I adopted after finding that meta tags were visible in the browser but missing from the initial HTML.

How I Verify BreadcrumbList

BASH - Check BreadcrumbList in raw HTML
curl -s \  https://icanup.com.ua/en/posts/example-slug \  | grep -A 40 '"BreadcrumbList"'

After checking the raw HTML, I inspect the JSON-LD payload itself.

I care not only about syntax validity but also about whether the schema matches the real visible breadcrumb hierarchy.

What I Verify in Tests

  • BreadcrumbList JSON-LD is present in the page source.
  • Visible breadcrumbs and structured hierarchy have the same order.
  • Positions start at 1 and remain sequential.
  • Navigable items use localized public URLs.
  • The current non-clickable page does not receive a fake item URL.
  • A Post hierarchy contains the expected category.
  • Nested category ancestors remain in the correct order.
  • UK and EN use the corresponding localized labels and URLs.
  • Static pages do not receive a Post-specific hierarchy.

What I Would Avoid

  • Do not build a separate breadcrumb tree only for SEO.
  • Do not use a database ID as the ListItem position.
  • Do not hardcode locale prefixes inside the JSON-LD builder.
  • Do not mechanically add an item URL to the non-navigable current page.
  • Do not use UK labels inside the EN schema.
  • Do not skip parent categories that are present in visible navigation.
  • Do not rely only on client-side JSON-LD insertion.

For me, BreadcrumbList is good when it does not invent a hierarchy for the search engine and instead describes the navigation the application already shows to the user.

What I Kept for Next Time

  • Build structured breadcrumbs from the same normalized collection used by the visible UI.
  • Generate positions from the ordered path.
  • Use application routing for item URLs.
  • Localize both URLs and breadcrumb labels.
  • Do not add the item property when the breadcrumb is not navigable.
  • Preserve category ancestors as the real hierarchy chain.
  • Verify BreadcrumbList in the initial server-rendered HTML.

Conclusion

In ICanUp, BreadcrumbList is not a separate SEO navigation system.

The visible breadcrumbs already know the hierarchy, localized labels, and public links. Structured data simply translates that contract into Schema.org BreadcrumbList.

Positions come from the ordered path, navigable items use localized URLs, and the current non-clickable page does not receive fake navigation semantics.

The result is one site hierarchy shown to users and search engines through different representations.