React SPA on Cloud Run
A project recipe for a React / TypeScript single-page application backed by a REST API, authenticated with Firebase, and hosted on Google Cloud Run. Use this as a blueprint when estimating, scoping, and delivering a typical B2B internal tool or admin panel.
Project Overview
This recipe targets the most common Aliz frontend engagement: a client-side rendered CRUD application behind authentication. Think user management dashboards, back-office tools, or internal admin panels — projects where SEO is irrelevant and rich interactivity matters.
| Decision | Choice |
|---|---|
| Rendering | Client-side SPA |
| Hosting | Google Cloud Run |
| Language | TypeScript |
| UI framework | React |
| Backend integration | REST API |
| Auth | Firebase Authentication |
| Primary state model | Server state via TanStack Query |
Tech Stack
Core
| Library | Role |
|---|---|
| React | UI library |
| TypeScript | Type-safe JavaScript |
| Vite | Build tool and dev server |
Styling & UI
| Library | Role |
|---|---|
| Tailwind CSS | Utility-first CSS |
| shadcn/ui | Component collection (Radix + Tailwind) |
State & Data
| Library | Role |
|---|---|
| TanStack Query | Server state and data fetching |
| Zustand | Client state management |
Routing & Forms
| Library | Role |
|---|---|
| React Router | Client-side routing |
| React Hook Form | Form state management |
| Zod | Schema validation |
| date-fns | Date utilities |
| Sonner | Toast notifications |
Build & Testing
| Library | Role |
|---|---|
| Vitest | Unit and integration testing |
| Playwright | End-to-end testing |
All core choices align with the Recommended Tech Stack. See individual pages for rationale and alternatives.
Architecture Overview
The application follows a standard SPA architecture. The browser loads the React application from a container running on Cloud Run. All data flows through a REST API, and Firebase handles user authentication.
The user's browser downloads the SPA bundle from Cloud Run (served via nginx). The SPA communicates directly with the REST API for data and with Firebase for authentication. Firebase-issued tokens are forwarded to the API so the backend can verify the caller's identity.
SPA Component Architecture
Inside the SPA, the application is organized around an app shell, a router, and self-contained feature modules. Each feature module owns its pages, hooks, and API calls.
The App Shell wraps everything in the required providers (auth, query client, state). React Router splits traffic between public routes (login) and protected routes that require authentication. Feature modules are self-contained — each one groups its pages, data-fetching hooks, and types together rather than scattering them across top-level folders.
Key Patterns
API Layer. A centralized fetch wrapper handles injecting the Firebase auth token into every outbound request and provides consistent error handling. Feature modules define their own TanStack Query hooks on top of this wrapper, following the query key factory pattern. This means each feature declares a structured set of query keys (for lists, details, filtered views), which makes cache invalidation predictable — when a mutation succeeds, you invalidate by the relevant key prefix and TanStack Query refetches automatically.
Authentication. Firebase Auth is wrapped in an AuthProvider context that sits near the top of the component tree. A custom hook exposes the current user and loading state to any component that needs it. A ProtectedRoute wrapper checks authentication status before rendering child routes — unauthenticated users are redirected to the login page with their intended destination preserved for post-login redirect. The auth token is retrieved asynchronously and attached to API requests via the centralized fetch wrapper.
State Management. TanStack Query owns all server-derived data — API responses, pagination cursors, cache timestamps. Zustand is reserved for client-only state: sidebar open/closed, theme preference, UI flags. The key rule is to never duplicate server state into a Zustand store. If data comes from an API, TanStack Query should be the single source of truth.
Always enforce authorization server-side. Frontend route guards improve UX but are trivially bypassed. The ProtectedRoute pattern prevents UI flicker and accidental navigation — it does not provide security. See Security.
Task Breakdown
| Epic | Description | Ballpark (dev-days) |
|---|---|---|
| Project Setup & Tooling | Scaffold React + TypeScript project, configure build tooling, linting, formatting, folder structure, environment config, CI pipeline | 2–3 |
| Authentication & Authorization | Firebase Auth integration, login / register pages, auth context and protected route wrappers, token management | 3–5 |
| App Shell & Navigation | Main layout with sidebar / header, routing setup, breadcrumbs, user menu, responsive shell | 2–4 |
| API Integration Layer | Centralized API client with auth headers, TanStack Query configuration, error handling patterns, query key conventions | 2–3 |
| CRUD Module 1 (primary entity) | List page with table / search / filters / pagination, detail page, create / edit forms with validation — establishes reusable patterns | 4–6 |
| CRUD Modules 2–3 (additional entities) | Replicate patterns from Module 1 for 2–3 more domain entities (effort drops due to pattern reuse) | 3–6 |
| Dashboard & Reporting | Summary page with stat cards, 2–3 charts, date/period filters | 3–5 |
| Polish & UX | Loading / error / empty states, toast notifications, responsive tweaks, accessibility basics | 2–4 |
| Testing | Unit tests for critical logic, component tests, E2E tests for core flows (login, CRUD) | 3–5 |
| Deployment & Infrastructure | Containerized build, Cloud Run deployment, nginx SPA config, CI/CD pipeline, custom domain | 2–3 |
Ballpark Totals
| Metric | Range |
|---|---|
| Total effort | 25–45 dev-days |
| Duration (1 developer) | 5–9 weeks |
| Duration (2 developers) | 3–5 weeks |
These are baseline estimates for a straightforward CRUD application with standard complexity. Apply complexity multipliers for drag-and-drop, real-time features, complex forms, i18n, or strict accessibility requirements. Always present estimates as ranges.
What's Not Included
- Backend API development
- UX/UI design and Figma work
- Internationalization — add +10–20% if needed
- WCAG accessibility audit — add +15–30%
- Complex data visualizations beyond basic charts
- Infrastructure provisioning (Terraform, networking)
- Project management overhead (meetings, demos) — typically +20–30% (see Common Pitfalls)
Deployment Overview
The SPA is deployed as a containerized static site on Google Cloud Run. The build uses a multi-stage Docker approach: a Node.js stage installs dependencies and runs the production build, producing a set of static assets. A second stage copies those assets into an nginx image that serves them.
The nginx configuration handles three concerns. First, SPA fallback routing — any path that doesn't match a static file is rewritten to the root index page so client-side routing works correctly. Second, compression — gzip is enabled for text-based assets to reduce transfer size. Third, caching and security headers — hashed asset files get long-lived cache headers, and standard security headers (frame options, content type sniffing protection, referrer policy) are applied to all responses.
Cloud Run serves the container with automatic TLS, scales to zero when idle (no cost for unused capacity), and requires no infrastructure management. The container must listen on the port specified by the PORT environment variable (default 8080). For latency-sensitive applications, setting a minimum instance count of one avoids cold-start delays. Choose a region close to your users for the best performance.
For CI/CD, a GitHub Actions workflow builds the Docker image and deploys it to Cloud Run on every merge to the main branch. The pipeline typically includes a lint/test step before the deploy step.
The deploy-cloudrun GitHub Action handles GCP authentication and deployment in a single workflow step, simplifying the CI/CD pipeline significantly.
Further Reading
Internal docs:
- Estimation — techniques and complexity factors
- Requirement Engineering — project requirements checklist
- Design Systems — choosing a design approach
- Rendering — rendering strategy comparison
- Deploy — other hosting options
- Testing Strategy — testing layers and tools
- Recommended Tech Stack — full stack overview
External resources: