React SPA Development: Best Practice Guide
This article provides detailed content.
Building SPAs with React has moved a long way from the "start with create-react-app" days. In 2026, with React 19, a mature ecosystem, and tools like Vite, Zustand, and TanStack, serious single-page applications can be built sustainably. This article covers component architecture, state management, performance, and testing discipline for scalable React SPAs.
Component Architecture: Organization Determines Scalability
React is easy to write, hard to scale. In a 50,000-line codebase, component organization directly shapes maintainability. A practical structure:
- Feature-first folders:
features/auth/,features/checkout/— domain-based separation - Shared layer:
shared/ui/design system components,shared/hooks/,shared/utils/ - Presentational vs container: Business logic in hooks lets "dumb" components test easily
- Colocation: A component's tests, styles, and stories live next to it
Naming discipline: PascalCase filename, default export for the component, named export for types. Each component does one job — a 400-line component is a decomposition signal.
Barrel files (index.ts aggregating exports) are debatable — useful for small features, harmful at scale (tree-shaking breaks). Direct imports are often the better call.
State Management: A Deliberate Choice
React state management landscape in 2026:
- Local state (useState): Default first choice for component-local data
- Context API: For low-change global state like theme or auth. Costly for frequently-changing state
- Zustand: Minimal, hook-based, no boilerplate. Sweet spot for mid-to-large apps
- Redux Toolkit: Large teams, strict patterns, valuable devtools
- Jotai / Recoil: Atomic state, granular re-render control
- TanStack Query (React Query): Standard for server state. Should not be confused with client state
Critical distinction: client state (UI preferences, form inputs) and server state (API data) need different tools. Zustand + TanStack Query is a strong, disciplined pairing for a modern React SPA.
Routing: React Router vs TanStack Router
React Router v6/v7 has been the standard; TanStack Router has matured as an alternative through 2024-2025:
- React Router: Wide adoption, data loaders, nested routing. Broad ecosystem
- TanStack Router: Type-safe, optional file-based, loaders/search params first-class
Pick TanStack Router if type safety is a priority. For ecosystem maturity and onboarding ease, React Router remains a safe choice.
Performance Optimization
SPAs have a reputation for slow first loads. What changes that:
- Code splitting: Route-based
React.lazy+Suspense— each page has its own bundle - Image optimization: AVIF/WebP, responsive
srcset, lazy loading (nativeloading="lazy") - Virtualization: react-virtual or TanStack Virtual for 100+ item lists
- Memoization discipline: Use useMemo and useCallback to solve a problem, not preemptively
- React 19 Compiler: Auto-memoization — minimizes manual useMemo/useCallback use
- Bundle analysis: Monthly check with
vite-bundle-visualizeror equivalent
Core Web Vitals targets: LCP < 2.5s, INP < 200ms, CLS < 0.1. Critical for both UX and SEO.
Testing Strategy
A React SPA's test pyramid:
- Unit tests (Vitest, Jest): Pure functions, custom hooks, utilities
- Component tests (React Testing Library): Component render + interaction behavior
- Integration tests: Feature-level flows — auth flow, checkout flow
- E2E tests (Playwright): Critical user journeys, full browser
Testing discipline: every PR includes tests for the minimum changed code. 70-80% coverage is a realistic target. Forcing 100% is a trap — it creates pressure for low-value tests.
Minimize mocking: use MSW (Mock Service Worker) for network mocks instead of mocked backends. Fully-mocked backends miss real bugs in production.
Form Management
Forms have been a historic React pain point. Mature tools in 2026:
- React Hook Form: Minimal re-render, validation integration, wide usage
- TanStack Form: Type-first, validation-first
- Zod: Schema validation, single source of truth between form and API
A Real Architecture Example
A B2B dashboard SaaS app (~80K lines):
- Build: Vite + TypeScript + Tailwind
- Routing: React Router v7 + lazy routes
- State: Zustand (UI) + TanStack Query (server)
- Form: React Hook Form + Zod
- UI: Custom design system on top of Radix UI primitives
- Tests: Vitest + RTL, Playwright for critical paths
- Deploy: Cloudflare Pages, API separate (Node + Fastify)
Tolga Ege - Senior Mobile & Web Developer, Founder of CreativeCode
Mobile App, Web Development, AI, SaaS