SEO

BreadcrumbList without errors: positions, URLs, tests

BreadcrumbList is an ordered list of steps from home to the current page. Three properties matter: itemListElement, position and item; the rest is decoration.

Key points

  • BreadcrumbList is an ordered list: it needs itemListElement, and every ListItem needs a position and an item that is a URL.
  • This site's article page declares 3 items, the first being Home pointing at /it, read on 1 November 2026.
  • A trail in the markup that differs from the visible trail is misleading markup, and it almost always comes from a template.

BreadcrumbList is the markup that tells Google where the page sits in the site hierarchy: an ordered list that starts at the home page and ends on the current one. You add it as JSON-LD and it has to match the trail people see.

The three properties that matter

The documentation on breadcrumbs, read on 1 November 2026, asks for itemListElement as an ordered array. Each ListItem carries position and item, the URL of the page it represents; name is the label people read. The same page says Google uses breadcrumb markup in the body of a page to categorise the content in results, and that the feature is available on desktop in every language and region where Search exists.

A correct block

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://ui.eneawork.it/it"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Blog",
      "item": "https://ui.eneawork.it/it/blog"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "BreadcrumbList senza errori",
      "item": "https://ui.eneawork.it/it/blog/breadcrumb-schema-guida"
    }
  ]
}

Three items are the smallest useful shape: home, section, page. Positions run consecutively from 1, the name is what the reader sees, the URL returns 200. The last item gets an item like the others: it is the current page, and the current page has a URL.

Checking positions and URLs

cat > check-breadcrumb.mjs ]*application\/ld\+json[^>]*>([\s\S]*?)/g)];
const nodes = blocks.flatMap(([, raw]) => {
  const data = JSON.parse(raw);
  return data["@graph"] || [data];
});
const crumbs = nodes.find((n) => n["@type"] === "BreadcrumbList");
if (!crumbs) {
  console.log("no BreadcrumbList");
  process.exit(1);
}
let expected = 1;
for (const item of crumbs.itemListElement) {
  if (item.position !== expected) console.log(`position ${item.position}, expected ${expected}`);
  if (!item.name || !item.item) console.log(`item at ${item.position} misses name or item`);
  expected += 1;
}
console.log(`${crumbs.itemListElement.length} crumbs, positions ${expected - 1} checked`);
EOF
node check-breadcrumb.mjs https://ui.eneawork.it/blog/come-aggiungere-schema-markup

The script extracts the BreadcrumbList, checks that positions run consecutively from 1, and checks that every item has a name and a URL. A wrong position does not break the page: it makes the list ambiguous, and Google can drop the markup without telling you.

The errors you see in templates

  • Position starts at 0 because the array starts at 0: position is a schema.org number, not an index.
  • The last item is the current page but has no item, because “it is obviously this one”: item is needed on the last item too.
  • The visible trail says “Blog” and the markup says “Articles”: two trails, and only one of them is true.
  • The trail includes the site name as its first element: home is already home, the brand belongs in Organization.
  • The markup is built from a category array that changes with filters: a filtered page is not an extra level of the hierarchy.

The live record

In the catalogue the trail already exists as navigation: menu, navbar and /blocks show the same two-level shape. When the visible menu and the markup tell the same hierarchy, the breadcrumb is not extra data: it is the same information written twice. Checked on 1 November 2026.

The limitation

The breadcrumb is not guaranteed: Google may show the trail or the bare URL, and on a phone screen there is often no room. The markup stays useful because it describes the hierarchy, not because it draws a line in the result. On a site where one page has two possible trails, the documentation asks you to declare the one people see.

Where to continue

The link structure is in hub-and-spoke internal links; the canonical address of the page is in canonicalisation; the list Google reads is in sitemap index or single file. If the hierarchy comes from routes, the framework changes it: the comparison is in Remix or Next.js.

Frequently asked questions

Must the visible breadcrumb match the markup?

Yes. Google asks the markup to describe what people see: a trail in the markup that differs from the page is the definition of misleading markup.

How many levels can a breadcrumb have?

The schema.org vocabulary sets no limit, but a readable trail has two or three levels beyond home. Past that, readers stop reading and the result does not show it.

Does every page need a breadcrumb?

It is needed where a page has a parent. The home page has no trail, and a list of one item adds no information.

Can I generate the breadcrumb from the router?

Yes, and that is the most reliable way: nested routes already carry the hierarchy. Watch filters and parameters, which are not levels.

Does the breadcrumb improve ranking?

No, it is not a ranking factor. It changes how the result displays the trail and helps Google understand the site hierarchy.