ADR: UI Stack — React SPA on Cloudflare Pages¶
- Status: Accepted (recommendation from Claude, confirmed by Jeffrey)
- Date: 2026-04-23
- Decider: Jeffrey Lambert
- Project: Admin Dashboard Cloudflare Migration
Context¶
The new admin is an application, not a document site: operators will filter lists, open detail drawers, edit records, trigger jobs, and navigate between customer / subscription / billing views. Adventive's brand-guide dashboard reference (https://brandguide.adventive.com/dashboard) is clearly a rich, componentized interface in that application shape.
Two candidate UI stacks were evaluated:
- SPA (React/Vue/Svelte) on Cloudflare Pages, calling admin-api Worker.
- Workers-rendered HTML (HTMX / server-side components) — UI and API colocated in one Worker.
Decision¶
React + Vite + TypeScript, deployed as a static SPA on Cloudflare Pages, calling the admin-api Worker over HTTPS with a signed Cloudflare Access JWT.
Why React specifically¶
- Largest component / tooling ecosystem for operator-grade admin UIs (Radix, shadcn, TanStack Table, TanStack Query).
- Maps cleanly to the brand-guide dashboard visual language (card-based, filter-heavy, data-dense).
- Broad hiring / AI-assistance support — easiest to maintain long-term.
- TypeScript gives a single-language contract with the admin-api Worker (shared
zodor@hono/zod-openapitypes).
Vue and Svelte were viable but offered no compelling advantage for the operator persona or Adventive's current skillset. If Adventive already has a front-end standard I should match, that overrides this recommendation.
Why SPA vs server-rendered Workers HTML¶
| Dimension | SPA (chosen) | Workers-rendered HTML |
|---|---|---|
| Interactivity | Native to the model | Needs HTMX + Alpine for parity |
| Local dev loop | vite dev, fast HMR |
Full wrangler dev cycle |
| Deploy separation | UI and API ship independently | Single artifact (could be pro or con) |
| Offline / caching | Edge-cached static bundle | Per-request render |
| Fit for brand-guide UX | Strong | Workable but awkward for data-dense views |
| Complexity | SPA build pipeline | Simpler template rendering |
The SPA wins on interactivity, local dev, and UX fit for the target design. The Worker/HTMX approach would be simpler but is the wrong tool for this application shape.
Cloudflare Access integration¶
- Pages site sits behind a Cloudflare Access policy (same policy as the API, so operators sign in once).
- UI reads authenticated user from
Cf-Access-Authenticated-User-Emailheader on a protected/meendpoint it calls at load time. - API validates the Access JWT on every request — UI cannot bypass.
Consequences¶
Positive: - Clean presentation/API separation that was the whole point of the project. - Rapid iteration on UI without touching the API. - Easy to bolt a second consumer (e.g., mobile operator app, CLI) onto the same API later.
Negative / to-mitigate:
- Two deploy pipelines (Pages + Worker) — needs coordinated versioning / contract testing.
- SPA routing + Cloudflare Access may need a fallback _redirects rule so deep links survive auth.
- Initial bundle size needs attention — operators on slow networks shouldn't wait 3 seconds.
Follow-ups¶
- Pick a component library (shadcn/ui is the current leading candidate — pairs well with Radix + Tailwind).
- Decide on state / data-fetching (TanStack Query is the strong default).
- Decide on routing (TanStack Router or React Router v7).
- Establish OpenAPI contract format so the UI can generate types from the API spec.