Skip to content
Twilight React Playground
ثيم رائدaren

useBreadcrumbs

hookBeginnerserverbrowserlive demo

Builds a page's breadcrumb trail from its loader data, with a Home-plus-title fallback and a remembered referrer on product pages.

import { useBreadcrumbs } from '@salla.sa/twilight-theme-engine/hooks';

In plain words

Breadcrumbs are the "Home › Chairs › Wooden chair" trail at the top of a page. Pass the page object your route's loader returned (a loader is the function that fetches a page's data before it renders), and you get an array of { name, url } to render as links.

When the loader sent no trail, you still get one: Home, then the page title.

Signature

function useBreadcrumbs(page?: Page | null): Breadcrumb[]

interface Breadcrumb { name: string; url: string }
// Page: { title; slug; id?; url?; parent?; breadcrumbs?: Breadcrumb[] }

Try it live

useBreadcrumbs() for a page object. With no trail from the loader, it builds Home plus the page title.Try this: turn the loader trail off: the fallback uses the translated Home label and the current path of this page.
Storefront canvas · en · LTR
  1. Home
  2. Help
  3. Shipping policy
[{"name":"Home","url":"/"},{"name":"Help","url":"/help"},{"name":"Shipping policy","url":"/help/shipping"}]
Controls
Loader sent page.breadcrumbs
What a theme writes
import { useBreadcrumbs } from '@salla.sa/twilight-theme-engine/hooks';
import type { Page } from '@salla.sa/twilight-theme-engine/types';

// `page` is what your route loader returned, e.g.
// { title: 'Shipping policy', slug: 'page-single', breadcrumbs: [...] }
export function Crumbs({ page }: { page: Page }) {
  const items = useBreadcrumbs(page);
  return (
    <ol>
      {items.map((crumb) => (
        <li key={crumb.url}>{crumb.name}</li>
      ))}
    </ol>
  );
}

Example

app/components/common/Crumbs.tsx
import { Link } from '@salla.sa/twilight-theme-engine/components/common';
import { useBreadcrumbs } from '@salla.sa/twilight-theme-engine/hooks';
import type { Page } from '@salla.sa/twilight-theme-engine/types';

export function Crumbs({ page }: { page: Page }) {
  const items = useBreadcrumbs(page);
  if (items.length === 0) return null;

  return (
    <nav aria-label="Breadcrumb">
      <ol className="breadcrumbs">
        {items.map((crumb, index) => (
          <li key={`${crumb.url}-${index}`}>
            {index < items.length - 1 ? <Link to={crumb.url}>{crumb.name}</Link> : <span>{crumb.name}</span>}
          </li>
        ))}
      </ol>
    </nav>
  );
}

How it behaves

  • With no page, it returns []. With a non-empty page.breadcrumbs, it returns them as they are.

  • Without page.breadcrumbs: [] on the home route, otherwise [{ name: t('common.titles.home'), url: '/' }, { name: page.title, url: location.pathname }].

  • On a page whose slug is product.single, the middle crumbs are replaced after mount by the last page the shopper visited, read from sessionStorage under breadcrumb-referrer: [first, referrer, last]. Every other page with a page object writes itself there.

  • Location and home detection come from the engine context (useLocation(), useIsHome()), never from window.location.

  • The engine's <Breadcrumb page={page} /> component is built on it.

  • It has no subpath of its own: import it from @salla.sa/twilight-theme-engine/hooks.

Gotchas

  • Its effect depends on the page object itself. Loader data is stable, but a page built inline during render is new every time, so the effect runs (and writes sessionStorage) on every render. Memoise hand-built page objects with useMemo.

  • On product pages the trail changes after mount (loader crumbs on the server, referrer crumbs in the browser), a visible swap on a slow device.

  • The fallback Home crumb's url is the literal '/', with no locale or store segment. Render crumbs with the engine Link (as the engine's BreadcrumbTrail does), not a plain <a>, which would leave the current language.

Related

Source and docs