How to Build a Patient Dashboard in React (FHIR-Native, in Minutes)
A step-by-step guide to building a patient dashboard in React using a pre-built, FHIR-native component — encounters, vitals, medications, and labs, with no charting code to write.
The patient dashboard is the screen that scares developers — and for good reason. It has to pull a patient's encounters, vitals, medications, lab results, notes, and prescriptions, turn streams of FHIR data into readable charts and lists, and keep it all consistent. Built from scratch, that's weeks of work. With a pre-built, FHIR-native component, it's one line and a few minutes. This guide shows you how, step by step.
The toolkit we recommend is ClinikAPI — a FHIR-native platform with a React component library. Here's why we suggest it up front:
- Free to start: Get your API keys in seconds — no credit card needed.
- One component:
PatientDashboardrenders the whole patient overview. - No FHIR code: It reads FHIR resources and displays them for you.
- Themeable: A
themeprop makes it fit your app's look. - Compliant: HIPAA-compliant, SOC 2-audited, with a signed BAA.
Quick Answer
To build a patient dashboard in React, drop in a pre-built FHIR-native component instead of building the screen yourself. With ClinikAPI's @clinikapi/react library, you add the PatientDashboard component, point it at a small route on your own backend (the proxyUrl), and pass a patientId. It renders the patient's encounters, vitals, medications, labs, notes, and prescriptions automatically — no charting, no data fetching, no FHIR handling on your side. The proxy route keeps your secret API key safe on the server, since the SDK is server-side only. Once the proxy is set up, adding the dashboard takes minutes, and a theme prop makes it match your app.
See the components live
Why this screen is hard from scratch
A "simple" patient dashboard hides a lot of work:
- Fetching FHIR data — encounters, observations, medications, each a different resource.
- Charting — turning vitals and labs into readable trends.
- Layout and state — keeping it all consistent as data loads and changes.
- Accessibility and theming — clinical UIs must be accessible and on-brand.
That's why teams lose weeks here. A pre-built component does all of it.
Step 1: Install the packages
You need two packages: the React components for the frontend, and the SDK for your backend.
npm install @clinikapi/react # frontend components
npm install @clinikapi/sdk # backend SDK (server-side only)
Step 2: Set up the backend proxy
Here's the important security rule: the SDK uses a secret API key (clk_live_… / clk_test_…) and must run only on your server. The components never call the API directly — they POST a { action, data } payload to a route on your backend, and that route routes each action to the SDK. The route's URL is the proxyUrl.
Here's a complete proxy route (Next.js App Router shown; the same shape works in Express, Hono, etc.):
// app/api/clinik/route.ts — server-only
import { Clinik } from '@clinikapi/sdk'
// The secret key lives here, never in the browser.
const clinik = new Clinik(process.env.CLINIKAPI_SECRET_KEY!, {
baseUrl: 'https://api.clinikapi.com', // default
timeout: 30_000, // ms (default)
retries: 2, // default
})
export async function POST(req: Request) {
const { action, data } = await req.json()
// 1) Authenticate the request with YOUR auth system first.
// 2) Authorize: confirm this user may access data.id / data.patientId.
try {
switch (action) {
case 'patients.read':
return Response.json(await clinik.patients.read(data.id, { include: data.include }))
case 'observations.create':
return Response.json(await clinik.observations.create(data))
// …add the actions the components you mount actually need
default:
return Response.json({ error: 'Unknown action' }, { status: 400 })
}
} catch (err) {
// SDK error messages never contain patient data — safe to log.
return Response.json({ error: 'Request failed' }, { status: 500 })
}
}
Every SDK call resolves to { data, meta }. The meta object carries a requestId, the HTTP status, and rate-limit fields (rateLimitRemaining, rateLimitReset) you can surface in logs or back off on.
Never import the SDK or put your clk_live_… key in front-end code — it would ship to every visitor's browser. The proxy pattern keeps the key server-side while components stay client-side. Authentication and authorization are your responsibility in this route: confirm the signed-in user is allowed to read or write the patient in data before calling the SDK.
Just exploring? Set proxyUrl="demo" on any component to run it with mock data and zero backend — no route, no key, no API calls. It's the fastest way to see a component before wiring up the proxy.
Step 3: Drop in the dashboard
Now the easy part. Add the component, point proxyUrl at your backend route, and pass the patientId:
import { PatientDashboard } from '@clinikapi/react'
export function Chart({ patientId }: { patientId: string }) {
return (
<PatientDashboard
proxyUrl="/api/clinik"
patientId={patientId}
theme="light"
/>
)
}
That's the whole dashboard. It renders the patient's encounters, vitals, medications, labs, notes, and prescriptions — the screen that would have taken weeks.
Under the hood, the component POSTs { action: 'patients.read', data: { id, include } } to your proxyUrl, which calls clinik.patients.read() and returns the FHIR data. Every ClinikAPI component takes the same three props:
| Prop | Type | Required | Notes |
|---|---|---|---|
proxyUrl | string | Yes | Your backend route, or "demo" for mock data |
patientId | string | Yes | The patient to render |
theme | 'light' \</td> <td>'dark' | No | Defaults to light |
What you didn't have to build
Worth pausing on everything the component handled:
| You skipped | Because the component does it |
|---|---|
| FHIR data fetching | Reads Encounter, Observation, MedicationRequest, etc. |
| Charts and trends | Renders vitals and labs visually |
| Layout and state | Manages loading and display |
| Theming | theme prop matches your app |
You provided a patient ID and a proxy route. The component did the rest.
Product Insight: Why ClinikAPI Makes This Fast
A patient dashboard is the clearest example of ClinikAPI's value: the hardest screen in clinical software becomes a single component.
What you get:
PatientDashboard— the full overview, built and maintained for you.- 13 more components — intake, scheduling, vitals, labs, prescriptions, notes, and more, in the same library.
- The proxy pattern — your secret key stays server-side; components stay simple.
- FHIR underneath — your data stays standard and interoperable.
- Compliance included — HIPAA-compliant, SOC 2-audited, with a signed BAA.
See it running in the UI Library, and read Pre-Built Healthcare UI Components for the bigger picture.
Frequently Asked Questions
1. How do I build a patient dashboard in React?
Use a pre-built FHIR-native component. Add ClinikAPI's PatientDashboard, give it a proxyUrl (your backend route) and a patientId, and it renders the full patient overview.
2. What does PatientDashboard show?
Encounters, vitals, medications, lab results, clinical notes, and prescriptions — the full patient overview, from FHIR data.
3. Why the proxy pattern?
Security. The SDK's secret key must stay server-side, so components call your backend route (the proxyUrl), which uses the SDK. The key never reaches the browser.
4. Do I need to know FHIR?
No. The component reads FHIR resources and displays them. You just provide a patient ID and a proxy route.
5. Can I theme it?
Yes — a theme prop (like light or dark) makes it match your app.
Conclusion
The patient dashboard doesn't have to be the screen that eats your timeline. With a pre-built, FHIR-native component, you skip the charting, the data fetching, and the FHIR handling, and drop in a complete overview with one line — once a small backend proxy keeps your key safe. Provide a patient ID, pick a theme, and you have the hardest screen in clinical software, done in minutes.
Key takeaways:
- A patient dashboard from scratch is weeks of charting and FHIR work.
- ClinikAPI's
PatientDashboardrenders the whole overview from apatientId. - The proxy pattern keeps your secret key server-side, out of the browser.
- You don't need FHIR expertise — the component handles the data.
- A
themeprop makes it fit your app.
Ready to build? Explore the UI Library or get your free API keys.