Skip to Content
DocsBreadcrumbs

Breadcrumbs

Breadcrumbs are a user interface design pattern that helps users understand their current location within a website or application. They are a navigational aid that displays a hierarchical trail of links from the homepage to the current page. Breadcrumbs are commonly displayed horizontally near the top of the page and use separators (typically chevrons) to indicate hierarchy.

In OpenMRS, breadcrumbs are implemented in the index.ts file using the registerBreadcrumb or registerBreadcrumbs functions exported by the @openmrs/esm-framework package.

The registerBreadcrumb function takes an object with the following properties:

  • path (required): The URL path of the page
  • title (required): The text that will be displayed in the breadcrumb. This can be a string, a function that returns a string, or a function that returns a Promise resolving to a string. Functions receive route parameters as an argument.
  • parent (optional): The path of the parent breadcrumb to create a hierarchical trail
  • matcher (optional): A string or RegExp that determines whether the breadcrumb should be displayed. If omitted, the path value is used as the matcher. Useful for dynamic routes with parameters (e.g., /patient/:patientUuid)

When a user navigates to a registered page, the breadcrumb registry can resolve the appropriate trail for the current path. Registering breadcrumbs only stores the data; a page or layout still needs to render a breadcrumb UI.

Here is an example of a simple breadcrumb for a page (Core v5+):

import { registerBreadcrumb } from "@openmrs/esm-framework"; export function startupApp() { registerBreadcrumb({ path: `${window.spaBase}/myPage`, title: "My First Page", }); }

When a user navigates to the SPA path ${window.spaBase}/myPage (for example, /openmrs/spa/myPage), the breadcrumb will display: My First Page

Breadcrumbs can also represent a trail of hierarchical links from the site homepage to sub pages. To implement this, use the parent property to indicate what should be the parent breadcrumb.

Here is an example of breadcrumbs with ancestors/sub pages (Core v5+):

import { registerBreadcrumbs } from "@openmrs/esm-framework"; export function startupApp() { registerBreadcrumbs([ { path: `${window.spaBase}/myPage`, title: "My First Page", }, { path: `${window.spaBase}/myPage/subpage1`, title: "Subpage 1", parent: `${window.spaBase}/myPage`, }, { path: `${window.spaBase}/myPage/subpage2`, title: "Subpage 2", parent: `${window.spaBase}/myPage`, }, ]); }

When a user navigates to different pages, the breadcrumbs will display:

  • On ${window.spaBase}/myPage: My First Page
  • On ${window.spaBase}/myPage/subpage1: My First Page > Subpage 1
  • On ${window.spaBase}/myPage/subpage2: My First Page > Subpage 2

Dynamic breadcrumbs with route parameters

You can create dynamic breadcrumbs that use route parameters. The title function receives an array of matched route parameters. For example, to show a patient UUID in the breadcrumb:

import { registerBreadcrumb } from "@openmrs/esm-framework"; export function startupApp() { registerBreadcrumb({ path: `${window.spaBase}/patient/:patientUuid`, matcher: `${window.spaBase}/patient/:patientUuid`, // Optional: explicitly define the matcher title: (params) => { // params is an array of matched route parameters // For a path like /openmrs/spa/patient/<uuid>, params[0] is the patientUuid value return `Patient ${params[0]}`; }, parent: `${window.spaBase}/patients`, }); }

For more complex scenarios, you can fetch data asynchronously:

import { registerBreadcrumb } from "@openmrs/esm-framework"; export function startupApp() { registerBreadcrumb({ path: `${window.spaBase}/patient/:patientUuid`, title: async (params) => { const patientUuid = params[0]; // Fetch patient data and return the patient name const patient = await fetchPatient(patientUuid); return patient.name; }, parent: `${window.spaBase}/patients`, }); }

Rendering breadcrumbs

registerBreadcrumb and registerBreadcrumbs only register breadcrumb data. The current app shell does not mount breadcrumbs automatically and does not register a core extension for breadcrumbs-slot by default. If a page renders a shared breadcrumb slot, that slot only shows content when the distribution or the owning layout module assigns a breadcrumb-rendering extension to it.

import { ExtensionSlot } from "@openmrs/esm-framework"; export default function MyPage() { return ( <> <ExtensionSlot name="breadcrumbs-slot" /> {/* Your page content */} </> ); }

If you create a new page layout, confirm that something in the distro actually provides the breadcrumb extension for breadcrumbs-slot; otherwise the slot will render nothing.

For custom layouts, use getBreadcrumbsFor(path) from @openmrs/esm-framework to read the registered trail and render it with the UI component library used by that layout.

  • Breadcrumb data can be filtered based on the current path with getBreadcrumbsFor(path) or filterBreadcrumbs(list, path)
  • The breadcrumb trail follows the parent hierarchy, showing all ancestors up to the current page
  • The app-shell breadcrumb renderer collapses trails with more than 4 breadcrumbs by replacing the middle entries with ”…”
  • The app-shell breadcrumb renderer shows a loading indicator while function-based titles resolve
  • Rendered breadcrumbs are clickable links that navigate to their respective paths
Last updated on