By the end of this you’ll have two reusable, accessible breadcrumb systems you can drop into any Webflow project — one manual, for full control on simple sites, and one automatic, that reads the page URL and builds the trail itself for CMS and deeply nested structures.
Breadcrumbs sound trivial until you try to make them right: an ordered list so the order actually means something, a nav landmark a screen reader can announce, separators that don’t get read aloud, and no dead links pretending to be clickable. We’ll build both versions as real Webflow components with properties, so you tweak the whole trail from one place instead of editing every page by hand.
The manual version is perfect when your structure is shallow and you want to define each step yourself. The automatic one is built for scale — it works out the labels, the links, and even which segments aren’t real pages, all on its own.
How it works
The accessible foundation is the same for both. The trail is a nav element with aria-label="breadcrumb", so screen readers announce it as the breadcrumb, not a generic nav. Inside it goes an ordered list — not unordered — because breadcrumbs are sequential: home first, current page last, and that order is information. Each item is a list item holding a link, and the last link gets aria-current="page" to mark the page you’re on. Separators are decorative, so they’re kept out of the accessibility tree: the character separator (a >) carries aria-hidden="true", the icon separator is marked as decorative so it’s skipped, and the third option is a pure-CSS separator generated with a ::after rule (a / after every item except the last) — no extra element at all. All three exist so the component can offer the choice; you switch between them with a visibility property.
The manual component puts you in control. Once the structure is built you convert it into a Webflow component and expose everything as properties, grouped by purpose: a visibility and content property for each separator, and per-level properties for the labels, the links, and each item’s visibility. That’s what makes it reusable — on a shallow static page you might show only home plus the current page and hide the levels in between; on a blog post you show more. The important accessibility detail is the dead crumb problem: a URL like /blog/posts/post-slug has a posts segment that isn’t a real page. Left as-is it looks and behaves like a broken link. The fix is aria-disabled="true" on that link (exposed as a property so you set it per level) plus a short CSS rule that removes pointer events, inherits the surrounding text color, and drops the underline — so it reads as plain text, honestly.
The automatic component does the thinking for you. It starts from a stripped-down copy of the manual one — just the home item and a template for the current level — plus a small script. On load the script reads the current URL and splits it into segments on the slashes. For every segment except the first (home) and the last (current page) it creates a list item, uses the slug as the label with dashes swapped for spaces, and then tests whether that partial path resolves to a real page: if it does, it renders a real link; if it doesn’t — like a CMS collection path — it renders plain text instead, so you never ship a broken link. The last segment is left alone if you’ve typed a custom label (handy for binding a CMS field like the post title), or filled from the URL if you left it empty. That path-testing is also why you may see a harmless 404 in the console: the script pinged a segment, found nothing, and quietly downgraded it to text.
The script finds what it needs through two small logic attributes — one that marks the breadcrumb wrapper as the component, and one that marks the last list item as the current level. These are for the code, not for accessibility, and they come pre-set in the cloneable, so you don’t have to think about them.
How to use it
-
Clone the project. Grab the Webflow cloneable. It ships both components —
breadcrumb manualandbreadcrumb auto— with the accessibility attributes, the three separator styles, and the fully commented script already wired up. -
Build the trail (manual). A
navwitharia-label="breadcrumb"wraps an ordered list; each list item holds a link. Give the last linkaria-current="page". Add your separators (character witharia-hidden="true", a decorative icon, or the CSS::after), then select the whole wrapper and convert it to a component. -
Expose properties. Group them cleanly — visibility + content per separator, and per level a link, a text label, and a visibility toggle. On the intermediate levels also expose an
aria-disabledproperty (defaultfalse) for crumbs that don’t link anywhere. -
Configure per page (manual). Drop the component in, choose one separator (hide the other two), show only the levels you need, and set each level’s label and link. For a segment that isn’t a real page, set its
aria-disabledproperty totrueso it renders as plain text. -
Or go automatic. Drop in
breadcrumb auto, hide the separators you don’t want, and that’s it — the script builds the rest from the URL. Only touch the last level’s text property when you want to override a long slug (or bind it to a CMS field). -
Publish and test on the live link. Click through: home should reach the homepage, real segments should link, non-page segments should read as plain text, and the current page should be labelled and marked. A stray 404 in the console for a non-existent segment is normal.