

FHIR Viewer Mode for OHIF
The Open Health Imaging Foundation (OHIF) Viewer is an open-source, web-based platform for medical imaging. Most OHIF deployments reach their images over DICOMweb, talking directly to a PACS. The FHIR Viewer mode takes a different route: it treats a FHIR server as the system of record, resolving imaging data through ImagingStudy and DocumentReference resources and authenticating with SMART on FHIR.
The practical upshot is that OHIF can be launched from inside an EHR or RIS, in the context of a patient the clinician has already selected, with the same OAuth 2.0 session and scopes the rest of the clinical workspace uses.
Table of Contents
- What is FHIR?
- What This Mode Does
- Requirements
- Quick Start
- SMART on FHIR Launch Context
- Configuring the Client ID
- ECG Waveform Support
- Module Reference
- Connecting to a FHIR Server
- Installation Videos
What is FHIR?
Fast Healthcare Interoperability Resources (FHIR, pronounced "fire") is HL7's standard for exchanging healthcare data. Where DICOM models the image and its acquisition, FHIR models the surrounding clinical record — patients, encounters, orders, reports, and the studies that belong to them — as discrete resources served over a RESTful API with JSON payloads.
The two standards are complements rather than competitors, and the boundary between them is well-trodden:
- ImagingStudy is FHIR's pointer to a DICOM study. It carries the study and series UIDs, modality, body site, and the endpoint where the pixel data actually lives.
- DocumentReference can wrap or point at binary content, including DICOM instances held outside a conventional PACS.
- ServiceRequest is the order that led to the exam; DiagnosticReport is the read that came out of it.
- The DICOM SR to FHIR implementation guide defines how DICOM Structured Reports — the measurements and findings OHIF produces — map onto FHIR Observation resources, so results captured in the viewer can travel back into the clinical record as first-class FHIR data.
For a radiology deployment, this means the worklist, the order, the patient context, and the report can all live in the same FHIR server that the viewer authenticates against.
What This Mode Does
The fhir-viewer mode bundles a FHIR data source with a standard OHIF layout and a handful of extras contributed by the accompanying @ohif/fhir-viewer extension:
FHIR data source — a webApi data source that connects to FHIR R4 servers with SMART on FHIR auth, translating ImagingStudy and DocumentReference resources into OHIF-compatible study, series, and instance metadata. From the viewer's perspective the studies look ordinary; the retrieval path is what changes.
FHIR configuration panel — a fhirConfig panel in the right panel group for setting the FHIR server URL, supplying auth details, and testing the connection without editing config files or rebuilding.
ECG waveform viewport — renders DICOM ECG waveforms via dcmjs-ecg, registered alongside the default Cornerstone viewport.
DICOM ZIP export — a downloadDicomZip command, wired into the right-click context menu, that exports the current study as a ZIP of DICOM Part 10 files. It tries WADO-RS, WADO-URI, and blob URL strategies in turn.
Developer tooling — nof.logViewportData dumps full viewport state (camera, display sets, tools, measurements, performance) to the console; nof.inspectViewportState opens a modal with viewport properties, actors, and image data.
Hanging protocols and layout — a chestBodyPart body-part-aware protocol for chest imaging, and a minimalViewerLayout template for a streamlined viewer.
The mode also extends the default tool group with a Text annotation tool and configures toolbar sections with the measurement tools and the log/inspect buttons.
Route: /fhir-viewer
Requirements
This mode does not work standalone — it is a client, and it needs a server to talk to. Specifically:
- A FHIR R4 server exposing ImagingStudy and/or DocumentReference resources, plus a SMART configuration at .well-known/smart-configuration if you want the OAuth flow.
- CORS configured on that server to permit the viewer's origin. The mode uses a CORS-direct model: the browser calls the FHIR endpoints at their absolute origin, with no dev proxy in between. If your server won't send the right Access-Control-Allow-Origin headers, nothing will load.
- A registered SMART client — the server needs to know which app is asking for access, and the viewer needs the resulting client ID at runtime.
- Somewhere for the pixels to live. FHIR resources are metadata and pointers; the imaging bytes still come from a DICOMweb endpoint or from binary content referenced by the FHIR resources.
Known-good backends are documented below: Medplum and Node on FHIR.
Quick Start
Clone the extension and OHIF side by side, install each, then link with the OHIF CLI:
# the extension — install its own dependenciesgit clone https://github.com/node-on-fhir/ohif-fhir-viewercd ohif-fhir-viewerpnpm install --config.auto-install-peers=falsecd ..# OHIFgit clone https://github.com/OHIF/Viewerscd Viewersbrew install pnpm # if you don't already have pnpmpnpm install# link the extension and its companion mode, then runpnpm run cli link-extension ../ohif-fhir-viewerpnpm run cli link-mode ../ohif-fhir-viewer/modepnpm dev
Open http://localhost:3000/fhir-viewer.
Two things worth knowing:
- --config.auto-install-peers=false matters. The extension's peerDependencies (e.g. @ohif/core) resolve from the OHIF build at bundle time and must not be installed into the extension's own node_modules.
- The link-* commands write entries into OHIF's tracked platform/app/pluginConfig.json. That's fine for local development, but don't commit them — clones of Viewers don't contain this extension. unlink-extension and unlink-mode undo them.
Alternative: environment variable injection
Requires OHIF/Viewers#6143. With that PR's branch, a single environment variable replaces both link-* commands and leaves the Viewers working tree untouched — the bundled fhir-viewer mode is auto-detected:
# out-of-tree checkout (use an absolute path for the directory override)EXTRA_EXTENSIONS="@ohif/fhir-viewer=$PWD/../ohif-fhir-viewer" pnpm dev# or, if the extension is cloned inside extensions/, the name alone sufficesEXTRA_EXTENSIONS=@ohif/fhir-viewer pnpm dev
Mode auto-detection: when an EXTRA_EXTENSIONS package contains a mode/ subdirectory with a package.json, that mode is registered automatically. Use EXTRA_MODES only to override or add other modes explicitly.
See docs/INSTALL-COMPARISON.md for a full comparison of the installation patterns.
SMART on FHIR Launch Context
SMART App Launch is the specification that lets a third-party application be launched from an EHR with the user's session and the current patient already established. Rather than making the radiologist find the patient a second time in a second application, the EHR hands the viewer a launch context and the viewer picks it up.
This mode supports the standard EHR launch. The sequence looks like this:
- The EHR (or RIS) redirects the browser to the viewer with iss and launch parameters — for example, http://localhost:3200/fhir-viewer?iss=http://localhost:8103/fhir/R4&launch=<launch-id>.
- The viewer detects both parameters and discovers the server's SMART configuration at <iss>/.well-known/smart-configuration.
- It redirects to the server's authorize endpoint. The user authenticates (or an existing session is reused) and grants consent.
- The server redirects back with ?code=…&state=…. The viewer exchanges the code for a token at /oauth2/token and loads the patient's studies.
A typical scope set for the registered client is launch openid fhirUser patient/*.read.
The viewer also accepts iss without launch — ?iss=http://localhost:8103/fhir/R4 sets the FHIR server without triggering an OAuth redirect, which is useful for standalone testing when you want to inject a token by hand.
Configuring the Client ID
There is no build-time environment variable for the SMART client ID in a deployed viewer. OHIF is a static SPA, so the client ID has to arrive at runtime. The viewer reads it from three browser-native surfaces, most-specific wins:
- URL — ?client_id=<uuid> on the SMART launch. Per-launch; lets one deployment face multiple registrations.
- SMART Preferences panel — entered or registered in OHIF, saved to localStorage. Per-user.
- window.config — the smartClientId field in the data source configuration of the served app-config.js. Per-deployment.
For containerized deployments, the app-config.js value can be templated from an environment variable at container startup via the OHIF Docker entrypoint. Pure-static hosts edit the served config or use the Preferences panel.
For local development against a source checkout, the smartClientId in config/default.js can be overridden without editing that file by adding the following to Viewers/platform/app/.env:
SMART_CLIENT_ID=your-registered-client-id
The .env value takes priority over the config file. Restart the dev server after changing it.
ECG Waveform Support
The ECG viewport handles seven SOP classes:
| SOP Class | UID |
|---|---|
| 12-Lead ECG | 1.2.840.10008.5.1.4.1.1.9.1.1 |
| General ECG | 1.2.840.10008.5.1.4.1.1.9.1.2 |
| Ambulatory ECG | 1.2.840.10008.5.1.4.1.1.9.1.3 |
| Hemodynamic Waveform | 1.2.840.10008.5.1.4.1.1.9.2.1 |
| Basic Cardiac EP | 1.2.840.10008.5.1.4.1.1.9.3.1 |
| Arterial Pulse Waveform | 1.2.840.10008.5.1.4.1.1.9.5.1 |
| Respiratory Waveform | 1.2.840.10008.5.1.4.1.1.9.6.1 |
Module Reference
| Module Type | OHIF ID | Description |
|---|---|---|
| commandsModule | nof.logViewportData, nof.inspectViewportState, nof.textCallback, downloadDicomZip | Viewport logging, state inspector, text input, DICOM export |
| viewportModule | ecg-dicom | ECG waveform viewport |
| sopClassHandlerModule | ecg-dicom | Display set builder for ECG SOP classes |
| dataSourcesModule | fhir | FHIR R4 data source |
| panelModule | fhirConfig | FHIR configuration panel |
| hangingProtocolModule | chestBodyPart | Chest body-part hanging protocol |
| layoutTemplateModule | minimalViewerLayout | Minimal viewer layout |
| customizationModule | viewportContextMenu | Right-click context menu |
Dependencies: jszip ^3.10.1 for ZIP archive generation, dcmjs-ecg ^0.0.14 for ECG waveform parsing and rendering.
Connecting to a FHIR Server
Any conformant FHIR R4 server with SMART support and permissive CORS should work. Two open-source backends have step-by-step guides:
Medplum
Medplum is a good fit for the CORS-direct model — its development configuration ships with allowedOrigins: "*", so the browser calls the FHIR and FHIRcast endpoints directly with no dev proxy required. The guide covers running Medplum's server, app, Redis, and PostgreSQL locally; registering a ClientApplication with the viewer's redirect and launch URIs; creating test Patient, ServiceRequest, DiagnosticReport, and ImagingStudy resources; triggering the SMART launch from the patient's Apps tab; and verifying FHIRcast subscription and event delivery.
Node on FHIR
Node on FHIR can act as a full RIS backend for the viewer. The guide covers installing Meteor and the Honeycomb starter, adding the radiology-workflow package and companion workflow modules, registering an OAuth client (either through the /oauth-clients UI or by POSTing to /oauth/register), and completing a SMART launch from the RIS reading worklist.
→ Node on FHIR RIS Integration Guide
In both cases, the check that the CORS-direct path is working is the same: open DevTools → Network and confirm that FHIR requests go to the server's absolute origin with no /fhir-proxy in the path.
Installation Videos
A playlist of installation walkthroughs is available on YouTube:
→ OHIF FHIR Viewer — Installation Videos
Further Reading
- HL7 FHIR — the base specification
- SMART App Launch — the launch and authorization framework
- DICOM SR to FHIR — mapping structured reports onto FHIR resources
- ohif-fhir-viewer on GitHub — source, issues, and full documentation
