Skip to content

Tutorial — Build your first widget

What you'll build

By the end of this tutorial you'll have a Hello-Patient widget rendering in the patient detail sidebar of your sandbox tenant. It reads the current patient via the App Bridge and renders their display name.

Prerequisites

  • Node.js 20+
  • An OAuth client ID issued by your tenant administrator
  • A configured home tenant and sandbox tenant

If any of those aren't in place yet, work through Getting Started first, then come back here.

Step 1: Scaffold

Goal: get a working app skeleton on disk.

bash
orion init hello-patient --client-id <your-client-id>
cd hello-patient
npm install

The scaffold writes a TypeScript + React + Vite project with two files you'll touch most often:

  • orion-app.json — the manifest (slug, name, version, scopes, extensions)
  • src/main.tsx — the iframe entrypoint that boots the App Bridge

It also writes orion.config.ts for CLI configuration (clientId, host, sandbox URL, tenant UUID, app slug) and a starter dist/ ignore. You won't need to edit those for this tutorial.

Step 2: Configure tenants

Goal: point the CLI at your tenants and authenticate.

Set the home tenant — the one that hosts the marketplace and is the target for orion publish:

bash
orion config host

Set the sandbox tenant — the one orion dev tunnels into for live development:

bash
orion config sandbox

Then sign in:

bash
orion login

A browser opens to the OAuth authorize page. Approve it and the CLI stores your tokens locally.

Step 3: Generate the widget

Goal: register a widget extension in the manifest and scaffold its component file.

bash
orion generate widget hello-patient --mount-point patient-detail-sidebar

This adds an entry to the extensions.widgets[] array in orion-app.json:

json
{
    "extensions": {
        "widgets": [
            {
                "id": "hello-patient",
                "title": "Hello Patient",
                "mountPoint": "patient-detail-sidebar",
                "component": "HelloPatient"
            }
        ]
    }
}

It also scaffolds the component into something like src/widgets/HelloPatient.tsx. The exact file path is printed by the command — open the file it shows you.

patient-detail-sidebar is one of the available widget mount points. See the manifest schema for the full set of supported slots.

Step 4: Edit the component

Goal: read the current patient from the App Bridge and render their name.

Replace the body of the generated widget with:

tsx
import { usePatient } from '@orion-ehr/app-bridge';

export default function HelloPatient() {
    const patient = usePatient();
    if (!patient) return <p>No patient in context.</p>;
    const name = `${patient.firstName ?? ''} ${patient.lastName ?? ''}`.trim();
    return <p>Hello, {name || 'there'}.</p>;
}

usePatient() returns the live PatientStub from the App Bridge — { id, uuid, firstName, lastName } — or null when the user isn't in patient context. The full hook surface (theme, encounter, user, toasts, navigation) is documented in the @orion-ehr/app-bridge README.

Step 5: Run dev

Goal: see your widget render live in the sandbox tenant.

bash
orion dev

Vite starts locally. The CLI opens a Cloudflare tunnel and registers it with your sandbox tenant so the host EMR can iframe your in-progress build. The terminal prints the tunnel URL once it's live.

In a browser, sign into your sandbox tenant, open any patient chart, and look at the patient detail sidebar. Your widget mounts there and displays the patient's name. Edits to HelloPatient.tsx hot-reload inside the iframe.

Step 6: Validate

Goal: catch manifest, bundle, and scope issues before you publish.

bash
orion validate

A clean run reports no errors. If validation fails, the CLI prints which file and which manifest field needs attention. Fix and re-run until it passes — orion publish re-runs validate internally, so this just gives you faster feedback during development.

Step 7: Build and publish

Goal: ship a versioned bundle to the marketplace for review.

bash
orion build
orion publish

publish re-runs validation, archives dist/ and your source, prompts for a changelog, and submits the bundle for marketplace review.

A few things to know:

  • The manifest version is semver and must change between publishes. Bump it before re-publishing or the upload is rejected.
  • The changelog prompt is required; this is what reviewers see first.
  • After upload, the version enters a review queue. It does not appear in the marketplace until a reviewer approves it.

Step 8: Watch the review

Goal: track review status and read reviewer feedback.

List your published versions and their states:

bash
orion versions list

Once a reviewer posts feedback or makes a decision, read the full thread:

bash
orion versions show 1.0.0

If the reviewer asks for changes, fix the code, bump the version, and run orion publish again.

What to try next

  • Development commands — full reference for orion dev, orion generate, and friends.
  • Manifest reference — every field in orion-app.json explained.
  • Wire up FHIR — once context-only widgets feel comfortable, add SMART-on-FHIR REST calls for clinical data. See your tenant's broader Orion developer documentation for the FHIR surface.

Documents @orion-ehr/cli v0.0.15 — released under the MIT License.