Components

Breadcrumb

AppBreadcrumb — the AntD breadcrumb trail at the top of every page: prior crumbs link back, the current page renders in primary text, a Divider follows it, and both hide on mobile where the header shows the page name instead.

Source in the MFEs
rate-fe AppBreadcrumb (canonical)administrator-fe AppBreadcrumb.vuereference-fe AppBreadcrumb.tsxrate-fe layout.scss (visibility)administrator-fe layout.scssrate-fe breadcrumb constants

AppBreadcrumb

Component

Page breadcrumb with i18n keys, priority ordering, and a mobile page-title broadcast.

Download .zip
Install tosrc/components/Requiresantdreact-router-dom
  • TSXAppBreadcrumb.tsx
  • MDREADME.md

Live example

The trail sits at the very top of the page inside .app-breadcrumb. Prior crumbs are clickable links; the last crumb (the current page) is plain text coloured text-primary. A Divider with mt-1 mb-1 always follows it, separating the trail from the page content.

Preview
Page content starts here.
// Usage in rate-fe/src/pages/Dashboard/index.tsx
<div className="w-100">
  <AppBreadcrumb items={breadcrumbItems} translate={t} />
  <Divider className="mt-1 mb-1" />
  <Space direction="vertical" size={16} style={{ width: '100%' }}>
    ...content
  </Space>
</div>

With close button (showBack)

Edit / create / view pages pass showBack — a CloseOutlined icon trails the breadcrumb on the right and calls navigate(-1).

Preview

Component

AppBreadcrumb ships per module: rate-fe is the canonical React version; reference-fe and report-fe carry near-identical copies; administrator-fe has the Vue equivalent. Items are sorted by their optional priority, and the last crumb’s label is broadcast on rvn-active-page-title so the header can show it on mobile.

// React: rate-fe/src/components/AppBreadcrumb/index.tsx (canonical)
import { Breadcrumb } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { useEffect, useMemo } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { emitMessage } from '@/hooks/useMessageBus';

interface AppBreadcrumbProps {
  items: BreadcrumbItem[];
  translate?: (key: string) => string;
  showBack?: boolean;
}

const AppBreadcrumb = ({ items, translate, showBack = false }: AppBreadcrumbProps) => {
  const navigate = useNavigate();

  const orderedItems = useMemo(() => {
    return items.map((item, index) => ({ ...item, __index: index })).sort((a, b) => (a.priority ?? a.__index) - (b.priority ?? b.__index));
  }, [items]);

  const handleBack = () => navigate(-1);

  // Broadcast current page name (last crumb) to header, shown on mobile where breadcrumb is hidden
  const activeTitle = orderedItems.length ? resolveLabel(orderedItems[orderedItems.length - 1], translate) : '';
  useEffect(() => {
    emitMessage('rvn-active-page-title', { title: activeTitle });
  }, [activeTitle]);

  return (
    <div className="app-breadcrumb d-flex align-items-center mb-2 w-100">
      <Breadcrumb>
        {orderedItems.map((item, index) => {
          const label = resolveLabel(item, translate);
          const isLast = index === orderedItems.length - 1;
          return (
            <Breadcrumb.Item key={`${label}-${index}`}>
              {item.to && !isLast ? (
                <Link to={item.to}>{label}</Link>
              ) : (
                <span className={isLast ? 'text-primary' : undefined}>{label}</span>
              )}
            </Breadcrumb.Item>
          );
        })}
      </Breadcrumb>
      {showBack && (
        <CloseOutlined
          onClick={handleBack}
          className="cursor-pointer text-secondary ms-auto fw-bold"
          style={{ marginRight: 12, fontWeight: 'bold' }}
        />
      )}
    </div>
  );
};

Props

NameTypeDefaultDescription
itemsBreadcrumbItem[]The crumbs — each has label, labelKey, to and an optional priority used to reorder the trail. Defined per page in each module under constants/breadcrumb.ts.
translate(key: string) => stringOptional i18n function; labelKey is resolved through it, falling back to the raw label.
showBackbooleanfalseShows the CloseOutlined button on the right (edit/create/view pages). Clicking calls navigate(-1).
app-breadcrumbclassBase class on the wrapper div — the hook for mobile visibility control and the rate-reference-host exception.

Mobile behaviour

Below md (max-width 767.98px) the breadcrumb is hidden entirely — the header displays the current page name instead, fed by the rvn-active-page-title broadcast. The Divider that follows the breadcrumb must be hidden in the same media query. The one exception is the rate reference-data embedded page: .rate-reference-host .app-breadcrumb stays visible on mobile via a higher-specificity override.

/* rate-fe/src/assets/scss/layout.scss — .app-breadcrumb visibility */

/* Mobile (max-width: 767.98px): hide the breadcrumb — and the Divider
   that follows it — together. The header shows the page name instead. */
@media (max-width: 767.98px) {
  .app-breadcrumb {
    display: none;
  }
}

/* Exception: the rate reference-data embedded page keeps it visible on mobile.
   The higher-specificity selector beats the @media hide. */
.rate-reference-host .app-breadcrumb {
  display: block;
}

Notes

Hide the Divider with the breadcrumb
The Divider is always placed after <AppBreadcrumb /> and before the page content, and it must be hidden together with the breadcrumb in the mobile media query — forgetting it leaves an orphaned separator at the top of the page on mobile.
Title broadcast to the header
The last crumb’s label is emitted on rvn-active-page-title so header-fe can show the page name on mobile. rate-fe and administrator-fe emit via the useMessageBus hook (emitMessage); reference-fe dispatches a CustomEvent on window.
racar-fe diverges
racar-fe has its own Breadcrumb.tsx that auto-generates the trail from the route. It does not accept a generic items array and does not broadcast the page title — it is not part of the AppBreadcrumb pattern.