Skip to main content

Architecture

This page explains how the Member Portal is structured at a high level — how the layout is assembled, how authentication is enforced, and how pages communicate with each other.

Overall layout​

The app uses Mantine's AppShell to create the standard three-part layout:

┌─────────────────────────────────────────────┐
│ Header (52px) │
├──────────────┬──────────────────────────────┤
│ │ │
│ Sidebar │ Main Content │
│ (260px or │ (Router renders │
│ 60px mini) │ the active page) │
│ │ │
└──────────────┴──────────────────────────────┘
  • Header — shows the ChartChat logo, the current page title, and the profile dropdown. On mobile, also shows a hamburger menu.
  • Sidebar — shows navigation links grouped into Overview, My Health, and Account. Can be collapsed to icon-only mode on desktop.
  • Main — where each page renders. The <Router /> component decides which page to show based on the URL.

How authentication works​

App.tsx is the root component. It checks whether the user is logged in using the Medplum client:

medplum.isLoading()   → show nothing (waiting for session check)
medplum.getProfile() → null means not logged in → show public routes
medplum.getProfile() → returns Patient → show the full app

Public routes (visible without being logged in):

  • / → Sign In page
  • /register → Sign Up page
  • /setpassword/:id/:secret → Set Password page
  • /verifyemail/:id/:secret → Verify Email page
  • Any other URL redirects to / (sign in)

Protected routes (require login): Everything else — Dashboard, Lab Results, Medications, etc.

Provider hierarchy​

The app wraps everything in React context providers. Here is the order from outermost to innermost:

MedplumProvider              ← Medplum SDK context (FHIR client, auth session)
NotificationProvider ← persistent bell notifications (FHIR-backed)
HospitalContextProvider ← tracks hospital-level EHR connections
EhrContextProvider ← tracks vendor-level EHR connections (Epic, Healow, MEDITECH)
PatientViewProvider ← patient/expert view mode toggle
HeaderProvider ← lets any page set the header title and action button
IdleTimeoutGuard ← monitors inactivity and auto-logs out after timeout
AppShell ← the visual layout (header + sidebar + main)
Router ← renders the active page

Each layer is independent — HeaderProvider only manages the title text, EhrContextProvider only manages vendor connections, HospitalContextProvider only manages hospital connections, etc.

Idle timeout​

Every authenticated session is protected by an idle timeout. If the patient has no mouse, keyboard, touch, or scroll activity for the configured duration (default: 15 minutes), a warning modal appears with a countdown. If they don't respond, they are automatically signed out.

The timeout duration can be changed in Settings. Available options: 15 min, 30 min, 60 min, 4 hours.

See useIdleTimeout for implementation details.

How pages set the header title​

Each page calls setPageTitle(...) from useHeader() when it mounts, and clears it when it unmounts:

const { setPageTitle, setHeaderAction } = useHeader();

useEffect(() => {
setPageTitle('Lab Results');
setHeaderAction(<SomeButton />);
return () => {
setPageTitle('');
setHeaderAction(null);
};
}, []);

This lets each page control what appears in the top header without the layout needing to know anything about the page.