2026 Boilerplate is a full-stack TypeScript web application with a React frontend and Express backend served together via vite-express. There is no database — authentication uses a hardcoded test user and state persists to encrypted localStorage.
Browser
│
├── React SPA (Vite HMR in dev)
│ ├── Pages (lazy-loaded via React Router)
│ ├── Redux Store (preferences, app slices)
│ │ └── Persistence Middleware → Encrypted localStorage
│ └── Chakra UI Components
│
└── HTTP requests
│
▼
Express Server (vite-express)
├── Auth Routes (/api/session + legacy /login/password, /logout) → Passport.js LocalStrategy
├── API Routes (/api/key) → Returns encryption key
├── Redirect Rules (legacy paths) → Config-driven HTTP redirects
├── Page Routes (/, /login, /product, etc.) → Served by Vite
└── Static Routes (/sitemap.xml)
src/
├── client/ # Frontend (React SPA)
│ ├── main.tsx # Entry: providers (Chakra, Redux, I18n, ErrorBoundary)
│ ├── App.tsx # Route definitions, preferences initialization
│ ├── hooks/ # Custom React hooks
│ ├── locales/ # Language translation files
│ ├── pages/ # Route page components
│ ├── redux/ # Redux slices, store, persistence middleware
│ ├── ui/ # Reusable UI components
│ └── utilities/ # Client-only utilities (encryption, constants)
│
└── server/ # Backend (Express)
├── main.ts # Entry: Express app, middleware, routes, vite-express
├── config/ # Constants and API error helpers
├── controllers/ # Request handlers
├── middleware/ # Auth middleware
├── routes/ # Route definitions
└── services/ # Business logic (auth verification)
- User submits credentials to
POST /login/password - Passport.js
LocalStrategyverifies against the hardcoded user - On success: JWT cookie (
token) is set, redirect to/product - On failure: redirect back to
/login - Protected routes use
ensureAuthenticatedmiddleware
- On app load,
initPreferencesasync thunk fetches encryption key from/api/key - If authenticated, the key is returned and used to decrypt
localStorage - On every Redux action, the persistence middleware encrypts and saves specified slices
- The persistence middleware in
store.tsis reusable — add persistence registrations for slices, don't create new middleware
- App-level catastrophic failure handling is managed by
ErrorBoundaryinsrc/client/main.tsx. - Route-level latency is managed by
Suspensearound lazy routes insrc/client/App.tsx. - Feature-level failures should use local boundaries when one section can fail independently (example: product counter actions).
- Feature-level latency should use local
Suspenseonly for independently-loading sections, not whole-page wrappers. - Use React 19
Activityaround loading fallback UI when representing active pending work.
The client uses src/client/utilities/ for browser-specific helpers. Server helpers live in src/server/services/ and src/server/config/. Keep client-only and server-only modules separated to avoid accidental cross-runtime imports.
| Layer | Choice | Why |
|---|---|---|
| Build | Vite | Fast HMR, ESM-native, simple config |
| Frontend | React 19 | Document metadata, hooks, Suspense |
| State | Redux Toolkit | DevTools, middleware, battle-tested at scale |
| Routing | React Router | Lazy loading, client-side navigation |
| UI | Chakra UI | Accessible, composable, good TypeScript support |
| Backend | Express 5 | Flexible, mature, large ecosystem |
| Auth | Passport.js | Strategy-based, extensible |
| Encryption | Native Web Crypto API | AES-GCM encryption for localStorage |
| Testing | Cypress | E2E testing with interactive runner |
| Linting | ESLint | Custom config with TypeScript + React rules |