Bulletproof React is a scalable React application architecture that provides opinionated guidelines and best practices for building production-ready React applications. The project includes three different implementations:
- React Vite: Modern Vite-based React application
- Next.js App Router: Next.js 13+ with App Router
- Next.js Pages: Traditional Next.js with Pages Router
The demo application is a team collaboration platform where users can:
- Create and join teams
- Start discussions within teams
- Comment on discussions
- Manage user roles (ADMIN/USER permissions)
Live Demo: https://bulletproof-react-app.netlify.app
# Navigate to desired app
cd apps/react-vite # or apps/nextjs-app or apps/nextjs-pages
# Install dependencies
yarn install
# Start development server
yarn dev
# Run tests
yarn test
# Run e2e tests
yarn test:e2e
# Lint code
yarn lint
# Build for production
yarn buildThe codebase follows a feature-based architecture organized as follows:
src/
βββ app/ # Application layer (routes, providers, router)
βββ components/ # Shared UI components
βββ config/ # Global configurations and env variables
βββ features/ # Feature-based modules (auth, discussions, comments, etc.)
βββ hooks/ # Shared React hooks
βββ lib/ # Preconfigured libraries (react-query, auth, etc.)
βββ testing/ # Test utilities and mocks
βββ types/ # Shared TypeScript types
βββ utils/ # Shared utility functions
Each feature should be self-contained:
src/features/awesome-feature/
βββ api/ # API calls and hooks for this feature
βββ components/ # Feature-specific components
βββ hooks/ # Feature-specific hooks
βββ stores/ # Feature-specific state
βββ types/ # Feature-specific types
βββ utils/ # Feature-specific utilities
- Strict mode enabled - All TypeScript strict checks are enforced
- Type-first approach - Define types before implementation
- Absolute imports - Use
@/prefix for all src imports (e.g.,@/components/ui/button)
- ESLint + Prettier configured for consistent formatting
- Kebab-case for file and folder names
- PascalCase for React components
- camelCase for functions and variables
- No cross-feature imports - Features should not import from each other
- Unidirectional flow - Code flows: shared β features β app
- Colocation - Keep related code as close as possible to where it's used
- Composition over props - Use children/slots instead of many props
- Single responsibility - Each component should have one clear purpose
- Extract render functions - Move complex JSX into separate components
- Limit prop count - Consider composition if accepting too many props
- Tailwind CSS is the primary styling solution
- Headless UI components using Radix UI primitives
- ShadCN/UI pattern - Components are copied into codebase, not installed as packages
- Use
useStatefor simple independent state - Use
useReducerfor complex state with multiple related updates
- Zustand for global application state (modals, notifications, themes)
- Keep state as close to usage as possible
- Avoid premature globalization
- React Query (TanStack Query) for all server state management
- MSW (Mock Service Worker) for API mocking during development
- Separate fetcher functions from hooks
- React Hook Form for form management
- Zod for form validation schemas
- Create reusable Form and Input components
Each API endpoint should have:
- Types & validation schemas for request/response
- Fetcher function using configured API client
- React Query hook for data fetching/caching
// api/get-discussions.ts
export const getDiscussions = (params: GetDiscussionsParams): Promise<Discussion[]> => {
return api.get('/discussions', { params });
};
export const useDiscussions = (params: GetDiscussionsParams) => {
return useQuery({
queryKey: ['discussions', params],
queryFn: () => getDiscussions(params),
});
};- Integration Tests (primary focus) - Test feature workflows
- Unit Tests - Test shared utilities and complex logic
- E2E Tests - Test critical user journeys
- Vitest - Test runner (Jest-compatible but faster)
- Testing Library - Component testing utilities
- Playwright - E2E testing framework
- MSW - API mocking for tests
- Test behavior, not implementation details
- Use real HTTP requests with MSW instead of mocking fetch
- Focus on user interactions and outcomes
- JWT tokens stored in HttpOnly cookies (preferred) or localStorage
- React Query Auth for user state management
- Automatic token refresh handling
- RBAC (Role-Based Access Control) for basic permissions
- PBAC (Permission-Based Access Control) for granular control
- Client-side authorization for UX (always validate server-side)
- Sanitize all user inputs before rendering
- Use DOMPurify for HTML content sanitization
- Validate and escape data at boundaries
- Route-level splitting - Lazy load pages/routes
- Avoid excessive splitting (balance requests vs. bundle size)
- Children prop pattern - Prevent unnecessary re-renders
- State colocation - Keep state close to where it's used
- State initializer functions - For expensive initial computations
- Lazy loading for images outside viewport
- Modern formats (WebP) with fallbacks
- Responsive images using srcset
- Global error interceptor in API client
- Automatic error notifications via toast system
- Automatic token refresh on 401 errors
- Error Boundaries at feature level (not just app level)
- Sentry integration for production error tracking
- Graceful fallbacks for broken components
- Vite for fast development builds and HMR
- TypeScript strict mode for compile-time safety
- ESLint + Prettier for code quality
- Deploy to CDN platforms: Vercel, Netlify, or AWS CloudFront
- Source maps uploaded to Sentry for error tracking
- Environment-specific configuration via env files
- Components:
kebab-case.tsx(e.g.,user-profile.tsx) - Hooks:
use-kebab-case.ts(e.g.,use-discussions.ts) - Utilities:
kebab-case.ts(e.g.,format-date.ts) - Types:
kebab-case.ts(e.g.,api-types.ts) - Folders:
kebab-casethroughout
- Pre-commit: ESLint, Prettier, TypeScript check
- Pre-push: Run test suite
- Ensure all checks pass before allowing commits
- Plop.js generators for consistent component creation
- Templates include component, stories, and test files
- Maintains consistent structure across team
- React 18 with concurrent features
- TypeScript in strict mode
- Vite or Next.js for build tooling
- Tailwind CSS for styling
- Radix UI for headless components
- Lucide React for icons
- TanStack Query for server state
- Zustand for client state
- React Hook Form + Zod for forms
- Vitest for unit/integration tests
- Playwright for E2E tests
- MSW for API mocking
- Storybook for component development
- Start with API types and validation schemas
- Create API fetcher functions and React Query hooks
- Build UI components with proper TypeScript integration
- Add integration tests covering the feature workflow
- Update routing and navigation as needed
- Use Plop generator:
yarn generate:component - Follow composition patterns over prop drilling
- Add Storybook stories for complex components
- Include unit tests for components with logic
- Start with local component state
- Lift to parent component if needed by siblings
- Move to global state only if needed across features
- Use React Query for all server state
This architecture prioritizes developer experience, maintainability, and scalability while following React and JavaScript best practices.