Skip to Content
DocsRecipesAdd links to the home page left panel

Adding links to the home page left panel

This guide will walk you through adding a link to the left panel  on the O3 home page. The O3 home page left panel contains links to various apps. Clicking a link typically navigates to the app’s landing screen.

This guide will walk you through adding a link to the Patient Lists  app, which is a frontend module that’s part of the O3 Patient Management monorepo. The Patient Lists app handles the creation and management of patient lists. This app has a landing screen that displays saved lists and a create-list workspace. Clicking a list navigates to the list details screen.

Below is a screenshot of the Patient Lists app landing screen:


Screenshot of the Patient Lists app landing screen

To achieve this, we’ll need to follow these steps:

Step 1: Set up routing for the Patient Lists app

We’ll begin by making sure that the Patient Lists app is properly set up to handle routing. Its Root component should look like this:

src/root.component.tsx
import React, { useEffect, useRef } from "react"; import { BrowserRouter, Route, Routes, useSearchParams } from "react-router-dom"; import { WorkspaceContainer, launchWorkspace2 } from "@openmrs/esm-framework"; import ListDetails from "./list-details/list-details.component"; import ListsDashboard from "./lists-dashboard/lists-dashboard.component"; function AutoLaunchPatientListWorkspace() { const [searchParams, setSearchParams] = useSearchParams(); const hasOpenedRef = useRef(false); useEffect(() => { const shouldOpenCreate = searchParams.has("create") || searchParams.has("new_cohort"); if (shouldOpenCreate && !hasOpenedRef.current) { hasOpenedRef.current = true; const rafId = requestAnimationFrame(() => { launchWorkspace2("patient-list-form-workspace"); setSearchParams({}, { replace: true }); }); return () => cancelAnimationFrame(rafId); } }, [searchParams, setSearchParams]); return null; } function RootComponent() { const basename = window.getOpenmrsSpaBase() + "home/patient-lists"; return ( <BrowserRouter basename={basename}> <AutoLaunchPatientListWorkspace /> <Routes> <Route path="/" element={<ListsDashboard />} /> <Route path="/:patientListUuid" element={<ListDetails />} /> </Routes> <WorkspaceContainer contextKey="patient-lists" /> </BrowserRouter> ); } export default RootComponent;

This BrowserRouter configuration lets O3 render the Patient Lists app landing screen at home/patient-lists and the list details screen at home/patient-lists/:patientListUuid. The workspace container keeps Patient Lists workspaces scoped to this page. The optional AutoLaunchPatientListWorkspace helper preserves compatibility with links that open the create-list workspace through query parameters.

We’ll begin by creating a dashboard metadata file and a helper function to create the dashboard link. First, create a dashboard.meta.ts file:

src/dashboard.meta.ts
export const dashboardMeta = { path: 'patient-lists', slot: 'patient-lists-dashboard-slot', title: 'patientLists', basePath: `${window.spaBase}/home`, } as const;

Next, create a createDashboardLink.tsx file:

src/createDashboardLink.tsx
import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { DashboardExtension, type DashboardExtensionProps, type IconId } from '@openmrs/esm-framework'; export const createDashboardLink = (config: Omit<DashboardExtensionProps, 'icon'> & { icon?: IconId }) => () => ( <BrowserRouter> <DashboardExtension path={config.path} title={config.title} basePath={config.basePath} icon={config.icon} /> </BrowserRouter> );
ℹ️

Note: The icon property is optional. If provided, it should be a valid Carbon icon name (e.g., "Calendar", "User", "List"). The icon will be displayed next to the link text in the left panel.

DashboardExtension translates the title value with useTranslation(). Keep a matching t() comment near the lifecycle export so translation extraction can find the display text.

Now, add a named export to the Patient Lists app index.ts file:

src/index.ts
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from "@openmrs/esm-framework"; import { configSchema } from "./config-schema"; import { createDashboardLink } from "./createDashboardLink"; import { dashboardMeta } from "./dashboard.meta"; import { setupOffline } from "./offline"; const moduleName = '@openmrs/esm-patient-list-management-app'; const options = { featureName: 'patient list', moduleName, }; export const importTranslation = require.context("../translations", false, /.json$/, "lazy"); export function startupApp() { setupOffline(); defineConfigSchema(moduleName, configSchema); } export const root = getAsyncLifecycle(() => import('./root.component'), options); // t('patientLists', 'Patient lists') export const patientListDashboardLink = getSyncLifecycle(createDashboardLink(dashboardMeta), options);
ℹ️

The root component typically uses getAsyncLifecycle for code splitting. The dashboard link uses getSyncLifecycle because it is lightweight and needs to be available as soon as the home page left panel renders.

Next, we’ll need to wire up the Patient lists link extension to the left panel. To do this, we’ll need to add the following extension definition to the Patient Lists app routes.json file:

{ "$schema": "https://json.openmrs.org/routes.schema.json", "backendDependencies": { "webservices.rest": ">=2.2.0" }, "extensions": [ { "name": "patient-lists-dashboard-link", "component": "patientListDashboardLink", "slot": "homepage-dashboard-slot", "meta": { "name": "patient-lists", "slot": "patient-lists-dashboard-slot", "title": "Patient lists" } }, { "name": "patient-lists-dashboard", "component": "root", "slot": "patient-lists-dashboard-slot" }, { "name": "list-details-table", "component": "listDetailsTable" }, { "name": "add-patient-to-patient-list-button", "component": "addPatientToPatientListMenuItem", "slot": "patient-actions-slot" } // ... ] }

Some things to note:

  • The component property is set to patientListDashboardLink, which is the named export that we defined in the previous step.
  • The name property is set to patient-lists-dashboard-link, which is the name of the extension.
  • The slot property is set to homepage-dashboard-slot, which is the name of the slot that we want to add the extension to. This slot is where the links that are displayed on the left panel of the O3 home page are rendered.
  • The meta property contains the dashboard configuration that the shell can inspect:
    • The name property is set to patient-lists, which is the path segment used in the URL.
    • The slot property is set to patient-lists-dashboard-slot, which is the name of the slot where the Patient Lists app dashboard content will be rendered.
    • The title property is set to Patient lists, which is human-readable dashboard metadata. The rendered link text comes from the title value passed to DashboardExtension in dashboardMeta.

The second extension definition registers the root component to render in the patient-lists-dashboard-slot when the Patient Lists app is active.

Step 4: Profit!

That’s it! You should now see a Patient lists link on the left panel of the O3 home page. Clicking on this link should navigate you to the Patient Lists app landing screen.

Last updated on