--- title: React SPA type: entity created: 2026-04-30 updated: 2026-04-30 sources: [gps-tracking-architecture] tags: [service, presentation-plane, frontend] --- # React SPA The end-user experience for operators and external participants. Single React application, role-based views, served as a static bundle. ## Why a separate SPA (not just the Directus admin UI) The Directus admin UI is generic CRUD over collections — right for back-office editing and operators who think in records, wrong for end users who think in domain concepts. A dedicated SPA delivers: - **Domain-shaped UX** — screens organized around the user's mental model. - **Independent deployment** — front-end ships on its own cadence. - **Targeted access control** — public/partner-facing routes without exposing the admin surface. - **Mobile/offline tuning** — bundles tuned for the actual user environment. ## Single app, role-based views One application serves multiple user types via role-based routing and conditional UI. All users authenticate through [[directus]]; the SPA receives a JWT, reads role, and renders appropriate navigation/screens. Splitting into multiple apps is only justified when user populations are genuinely disjoint (public site vs. authenticated console) or when bundle size for one audience harms another. ## Data access pattern The SPA talks **exclusively** to Directus: - REST/GraphQL via `@directus/sdk`. - WebSocket subscriptions via the same SDK. - JWT auth managed by the SDK; refresh handled transparently. **Never** talks to the [[processor]], [[tcp-ingestion]], [[redis-streams]], or [[postgres-timescaledb]] directly. This boundary lets the back-end evolve internally and keeps the security model coherent — every request goes through Directus's permission system. ## Recommended stack - **Vite + React + TypeScript** - **TanStack Router** — better TS support than React Router; optional file-based routing - **TanStack Query** — server state, caching, invalidation, optimistic updates - **@directus/sdk** — typed access + real-time - **MapLibre GL + react-map-gl** — open-source WebGL maps, no token needed - **shadcn/ui + Tailwind** — UI primitives - **Zustand** — client-only state (filters, UI prefs) - **react-hook-form + Zod** — forms and validation Covers the spectrum from form-heavy admin screens to real-time map dashboards without architectural changes between them. ## Real-time rendering - **Live maps with many markers**: React reconciler is not the bottleneck — drawing happens in WebGL via MapLibre, which manages features outside React's tree. The React layer manages subscriptions and feeds the map updates. - **High-frequency tabular updates** (live leaderboards, event feeds): split components so high-update areas re-render in isolation; use TanStack Query for live data; memoize at component boundaries that receive frequent updates. ## Failure mode UI unavailable → back-end unaffected. See [[failure-domains]].