React β NHS Quickstart
Perfect for thin internal tools: list views over KPIs, form-based admin, and embedding charts from APIs that sit in front of SQL Server. Keep secrets on the server; React only calls your internal API.
Great for: Developer Β· BI Analyst (with a service) Β· Data Scientist (model explorer).
βοΈ 10-minute installβ
- A) Vite (recommended)
- B) Prefer SSR? Use Next.js
Create a lightweight React app with Vite.
npm create vite@latest nhs-react -- --template react
cd nhs-react
npm install
npm run dev
Open the URL printed in the terminal.
For server-side rendering or built-in API routes, start with Next.js instead and call your FastAPI/Express backend server-side.
npx create-next-app@latest nhs-next --ts --eslint
π βHello NHSβ β call a KPI API and render a tableβ
Create .env.local (gitignored):
VITE_API_URL=http://localhost:8000
VITE_API_KEY=dev-local-key
Replace src/App.jsx with:
import { useEffect, useState } from 'react'
export default function App(){
const [rows, setRows] = useState([])
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
const url = (import.meta.env.VITE_API_URL || '') + '/kpi'
const headers = {}
if (import.meta.env.VITE_API_KEY) headers['x-api-key'] = import.meta.env.VITE_API_KEY
fetch(url, { headers, cache: 'no-store' })
.then(r => { if(!r.ok) throw new Error('Fetch failed'); return r.json() })
.then(d => setRows(d.rows || d))
.catch(e => setError(e.message))
.finally(() => setLoading(false))
}, [])
if (loading) return <main style={{padding:16}}><p>Loadingβ¦</p></main>
if (error) return <main style={{padding:16}}><p role="alert">Error: {error}</p></main>
return (
<main className="nhsuk-width-container" style={{padding:16}}>
<h2>NHS KPI</h2>
<small>Data last updated from API on page load.</small>
<table role="table" style={{borderCollapse:'collapse', width:'100%'}}>
<thead>
<tr>
<th style={{textAlign:'left'}}>Practice</th>
<th>Total</th>
<th>Attendance</th>
<th>Median wait (min)</th>
</tr>
</thead>
<tbody>
{rows.map(r => (
<tr key={r.practice_id}>
<td>{r.practice_id}</td>
<td>{r.total_appointments}</td>
<td>{r.attendance_rate != null ? (r.attendance_rate * 100).toFixed(1) + '%' : 'β'}</td>
<td>{r.median_wait_minutes ?? 'β'}</td>
</tr>
))}
</tbody>
</table>
</main>
)
}
Run the app:
npm run dev
This expects a local API (e.g., FastAPI or Express) with a
/kpiendpoint. See See also below.
π Charts & tablesβ
- A) TanStack Table (sortable)
- B) Plotly (clinical-style charts)
For sortable/filterable tables without heavy dependencies.
npm i @tanstack/react-table
// Example sketch (see TanStack docs for full usage)
/*
import { useReactTable, getCoreRowModel, getSortedRowModel, flexRender } from '@tanstack/react-table'
// define columns, data, and render a table with sortable headers
*/
Quick interactive charts backed by your API data.
npm i react-plotly.js plotly.js
/*
import Plot from 'react-plotly.js'
<Plot data={[{ type:'bar', x: rows.map(r=>r.practice_id), y: rows.map(r=>r.total_appointments) }]} layout={{ title:'Appointments by Practice' }} />
*/
π Auth, CORS, and SSOβ
- Keep SQL access on the server (FastAPI/Express). React talks to your API only.
- For dev, allow a temporary API key header. In prod, protect with Entra ID (Azure AD) and serve the UI behind SSO.
- Lock CORS on the API to your appβs origin (e.g., intranet domain).
π§± Styling & NHS brandingβ
- Use @nhsuk/frontend CSS for consistent typography and colour.
- Or Tailwind for utility classes; keep contrast and accessible focus states.
Global CSS import example (Vite)
@import "@nhsuk/frontend/dist/nhsuk.css";
Add to src/main.jsx:
import './main.css'
π³ Docker (optional for static hosting)β
For Vite builds, you can produce a static site and serve via NGINX.
Dockerfile
# Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Serve
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Deploy to Azure Static Web Apps, S3 + CloudFront, or behind IIS/NGINX on the intranet.
π IG & safety checklistβ
- No secrets in React code; everything sensitive stays server-side.
- Suppress small numbers at the API; donβt compute PHI in the browser.
- Version UI + API together; document endpoint owners and retention.
- Ensure component libraries meet a11y standards; test keyboard + screen reader paths.
π Measuring impactβ
- TTI and Lighthouse scores for key pages.
- Error rate from API calls (network + 5xx).
- Adoption: users per week; tasks completed.
- Change lead time: PR opened β deployed.
π See alsoβ
Whatβs next?
Youβve completed the Learn β React stage. Keep momentum: