Bulletproof React
Architecture patterns for building scalable, maintainable React applications. Based on bulletproof-react.
Core references
| Topic |
Description |
Reference |
| Project Structure |
Feature-based organization, unidirectional architecture, ESLint enforcement |
project-structure |
| Components & Styling |
Component hierarchy, wrapping 3rd party libs, headless vs styled libraries |
components-and-styling |
| API Layer |
API client, request declarations, query/mutation hook patterns |
api-layer |
| State Management |
Component, application, server cache, form, and URL state categories |
state-management |
| Error Handling |
Error boundaries, API errors, error tracking with Sentry |
error-handling |
| Testing |
Unit, integration, e2e strategies with Vitest, Testing Library, Playwright, MSW |
testing |
| Project Standards |
ESLint, Prettier, TypeScript, Husky, absolute imports, file naming |
project-standards |
| Security |
Authentication, token storage, XSS prevention, RBAC/PBAC authorization |
security |
| Performance |
Code splitting, data prefetching, state optimization, children pattern |
performance |
Project structure
Organize by feature, not by file type:
src/
├── app/ # Application shell (routes, providers, router)
├── assets/ # Static files (images, fonts)
├── components/ # Shared, reusable UI components
├── config/ # Environment variables, constants
├── features/ # Feature-based modules
├── hooks/ # Shared custom hooks
├── lib/ # Pre-configured library wrappers
├── stores/ # Global client state
├── testing/ # Test utilities, MSW handlers, factories
├── types/ # Shared TypeScript types
└── utils/ # Pure utility functions
Feature modules
features/users/
├── api/ # API functions and query hooks
├── components/ # Feature-specific components
├── hooks/ # Feature-specific hooks
├── types/ # Feature-specific types
└── utils/ # Feature-specific utilities
Rules:
- Features should not import from other features. Compose at the app level.
- Code flows one direction: shared → features → app.
- Promote to shared directories only when reused by 2+ features.
- Prefer direct imports over barrel re-exports for Vite tree-shaking.
Component hierarchy
Page Components → route-level, compose features, handle layout
└── Feature Components → feature-specific, business logic
└── UI Components → shared primitives, no business logic
API layer pattern
// Pure API function
function getUsers(params?: GetUsersParams): Promise<UsersResponse> {
return api.get("/users", { params });
}
// Query hook wrapping the API function
function useUsers(params?: GetUsersParams) {
return useQuery({
queryKey: ["users", params],
queryFn: () => getUsers(params),
});
}
State management boundaries
| State Type |
Solution |
Examples |
| Server state |
TanStack Query |
User data, posts, API responses |
| Client state (global) |
Zustand / Jotai |
Theme, sidebar open, user preferences |
| Client state (local) |
useState / useReducer |
Form inputs, toggles, modal open |
| URL state |
URL search params / router |
Filters, pagination, active tab |
| Form state |
React Hook Form |
Multi-step forms, validation |
Don't mix server and client state. Never copy query data into useState.
Error hierarchy
App Error Boundary → catches unrecoverable crashes
└── Route Error Boundary → catches route-level failures, shows retry
└── Feature Error Boundary → catches feature-specific errors
Testing strategy
| Layer |
Tool |
What to Test |
| Components |
Testing Library |
Render output, user interactions, a11y |
| Hooks |
renderHook |
State changes, side effects |
| API |
MSW |
Request/response handling, error states |
| Integration |
Testing Library + MSW |
Full feature flows (render → interact → verify) |
| E2E |
Playwright |
Critical user journeys |
Conventions
| Item |
Convention |
Example |
| Components |
PascalCase |
UserCard.tsx |
| Hooks |
camelCase, use prefix |
useUsers.ts |
| Utilities |
camelCase |
formatDate.ts |
| Types |
PascalCase |
User, CreateUserInput |
| Constants |
UPPER_SNAKE_CASE |
MAX_RETRIES |
| Directories |
kebab-case |
user-settings/ |
| Files |
kebab-case |
user-card.tsx |
Imports
Use path aliases to avoid deep relative imports:
import { Button } from "@/components/ui/button";
import { useUsers } from "@/features/users/api";
Configure @/ as the src/ alias in tsconfig.json.
1---2name: bulletproof-react3description: Bulletproof React architecture patterns for scalable, maintainable applications. Covers feature-based project structure, component patterns, state management boundaries, API layer design, error handling, security, and testing strategies. Use when structuring a React project, designing application architecture, organizing features, or when the user asks about React project structure or scalable patterns.4---56# Bulletproof React78Architecture patterns for building scalable, maintainable React applications. Based on [bulletproof-react](https://github.com/alan2207/bulletproof-react).910## Core references1112| Topic | Description | Reference |13| -------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------- |14| Project Structure | Feature-based organization, unidirectional architecture, ESLint enforcement | [project-structure](references/project-structure.md) |15| Components & Styling | Component hierarchy, wrapping 3rd party libs, headless vs styled libraries | [components-and-styling](references/components-and-styling.md) |16| API Layer | API client, request declarations, query/mutation hook patterns | [api-layer](references/api-layer.md) |17| State Management | Component, application, server cache, form, and URL state categories | [state-management](references/state-management.md) |18| Error Handling | Error boundaries, API errors, error tracking with Sentry | [error-handling](references/error-handling.md) |19| Testing | Unit, integration, e2e strategies with Vitest, Testing Library, Playwright, MSW | [testing](references/testing.md) |20| Project Standards | ESLint, Prettier, TypeScript, Husky, absolute imports, file naming | [project-standards](references/project-standards.md) |21| Security | Authentication, token storage, XSS prevention, RBAC/PBAC authorization | [security](references/security.md) |22| Performance | Code splitting, data prefetching, state optimization, children pattern | [performance](references/performance.md) |2324## Project structure2526Organize by feature, not by file type:2728```text29src/30├── app/ # Application shell (routes, providers, router)31├── assets/ # Static files (images, fonts)32├── components/ # Shared, reusable UI components33├── config/ # Environment variables, constants34├── features/ # Feature-based modules35├── hooks/ # Shared custom hooks36├── lib/ # Pre-configured library wrappers37├── stores/ # Global client state38├── testing/ # Test utilities, MSW handlers, factories39├── types/ # Shared TypeScript types40└── utils/ # Pure utility functions41```4243### Feature modules4445```text46features/users/47├── api/ # API functions and query hooks48├── components/ # Feature-specific components49├── hooks/ # Feature-specific hooks50├── types/ # Feature-specific types51└── utils/ # Feature-specific utilities52```5354**Rules:**5556- Features should not import from other features. Compose at the app level.57- Code flows one direction: **shared → features → app**.58- Promote to shared directories only when reused by 2+ features.59- Prefer direct imports over barrel re-exports for Vite tree-shaking.6061## Component hierarchy6263```text64Page Components → route-level, compose features, handle layout65 └── Feature Components → feature-specific, business logic66 └── UI Components → shared primitives, no business logic67```6869## API layer pattern7071```typescript72// Pure API function73function getUsers(params?: GetUsersParams): Promise<UsersResponse> {74 return api.get("/users", { params });75}7677// Query hook wrapping the API function78function useUsers(params?: GetUsersParams) {79 return useQuery({80 queryKey: ["users", params],81 queryFn: () => getUsers(params),82 });83}84```8586## State management boundaries8788| State Type | Solution | Examples |89| --------------------- | -------------------------- | ------------------------------------- |90| Server state | TanStack Query | User data, posts, API responses |91| Client state (global) | Zustand / Jotai | Theme, sidebar open, user preferences |92| Client state (local) | useState / useReducer | Form inputs, toggles, modal open |93| URL state | URL search params / router | Filters, pagination, active tab |94| Form state | React Hook Form | Multi-step forms, validation |9596**Don't mix server and client state.** Never copy query data into `useState`.9798## Error hierarchy99100```text101App Error Boundary → catches unrecoverable crashes102 └── Route Error Boundary → catches route-level failures, shows retry103 └── Feature Error Boundary → catches feature-specific errors104```105106## Testing strategy107108| Layer | Tool | What to Test |109| ----------- | --------------------- | ----------------------------------------------- |110| Components | Testing Library | Render output, user interactions, a11y |111| Hooks | renderHook | State changes, side effects |112| API | MSW | Request/response handling, error states |113| Integration | Testing Library + MSW | Full feature flows (render → interact → verify) |114| E2E | Playwright | Critical user journeys |115116## Conventions117118| Item | Convention | Example |119| ----------- | ----------------------- | ------------------------- |120| Components | PascalCase | `UserCard.tsx` |121| Hooks | camelCase, `use` prefix | `useUsers.ts` |122| Utilities | camelCase | `formatDate.ts` |123| Types | PascalCase | `User`, `CreateUserInput` |124| Constants | UPPER_SNAKE_CASE | `MAX_RETRIES` |125| Directories | kebab-case | `user-settings/` |126| Files | kebab-case | `user-card.tsx` |127128### Imports129130Use path aliases to avoid deep relative imports:131132```typescript133import { Button } from "@/components/ui/button";134import { useUsers } from "@/features/users/api";135```136137Configure `@/` as the `src/` alias in `tsconfig.json`.