Comprehensive React Module
Streamline your workflow with this skill, should, used, building. Includes structured workflows, validation checks, and reusable patterns for development.
Comprehensive React Module
A complete development skill for building modern React applications with hooks, context, suspense, server components, and performance optimization patterns. Covers the full spectrum from component architecture through state management to deployment-ready production patterns.
When to Use This Skill
Choose this skill when:
- Building new React applications from scratch with modern best practices
- Migrating class-based components to functional components with hooks
- Implementing complex state management across deeply nested component trees
- Setting up code splitting, lazy loading, and suspense boundaries
- Optimizing render performance with memoization and profiling
Consider alternatives when:
- Working exclusively with Next.js routing and SSR → use a Next.js-specific skill
- Building React Native mobile applications → use a React Native skill
- Need only UI component patterns → use a React UI patterns skill
- Working with a specific state library like Redux or Zustand → use that library's skill
Quick Start
# Create a new React project with Vite npm create vite@latest my-app -- --template react-ts cd my-app && npm install
// src/App.tsx — Modern React application shell import { Suspense, lazy } from 'react'; import { ErrorBoundary } from './components/ErrorBoundary'; import { ThemeProvider } from './contexts/ThemeContext'; import { AuthProvider } from './contexts/AuthContext'; const Dashboard = lazy(() => import('./pages/Dashboard')); const Settings = lazy(() => import('./pages/Settings')); export default function App() { return ( <ErrorBoundary fallback={<p>Something went wrong</p>}> <ThemeProvider> <AuthProvider> <Suspense fallback={<LoadingSkeleton />}> <Router /> </Suspense> </AuthProvider> </ThemeProvider> </ErrorBoundary> ); }
Core Concepts
Component Architecture Patterns
| Pattern | Purpose | Example |
|---|---|---|
| Container/Presentational | Separate logic from UI | UserContainer → UserCard |
| Compound Components | Shared implicit state | <Select><Option /></Select> |
| Render Props | Flexible rendering delegation | <DataFetcher render={data => ...} /> |
| Custom Hooks | Reusable stateful logic | useDebounce, useLocalStorage |
| Higher-Order Components | Cross-cutting concerns | withAuth(ProtectedPage) |
Custom Hook Patterns
// Reusable data fetching hook with caching function useFetch<T>(url: string, options?: RequestInit) { const [state, setState] = useState<{ data: T | null; loading: boolean; error: Error | null; }>({ data: null, loading: true, error: null }); useEffect(() => { const controller = new AbortController(); setState(prev => ({ ...prev, loading: true })); fetch(url, { ...options, signal: controller.signal }) .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }) .then(data => setState({ data, loading: false, error: null })) .catch(error => { if (error.name !== 'AbortError') { setState({ data: null, loading: false, error }); } }); return () => controller.abort(); }, [url]); return state; }
Context with Reducer Pattern
// Scalable state management without external libraries type Action = | { type: 'SET_USER'; payload: User } | { type: 'LOGOUT' } | { type: 'SET_THEME'; payload: 'light' | 'dark' }; function appReducer(state: AppState, action: Action): AppState { switch (action.type) { case 'SET_USER': return { ...state, user: action.payload, isAuthenticated: true }; case 'LOGOUT': return { ...state, user: null, isAuthenticated: false }; case 'SET_THEME': return { ...state, theme: action.payload }; default: return state; } } const AppContext = createContext<{ state: AppState; dispatch: React.Dispatch<Action>; } | null>(null); export function useApp() { const context = useContext(AppContext); if (!context) throw new Error('useApp must be inside AppProvider'); return context; }
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
strictMode | boolean | true | Enable React StrictMode double-rendering in dev |
suspenseFallback | ReactNode | <Spinner /> | Default loading component for Suspense boundaries |
errorBoundaryFallback | ReactNode | <ErrorPage /> | Default error fallback component |
codeSplitThreshold | string | 'route' | Split at route, component, or feature level |
memoizationStrategy | string | 'selective' | Memoize all, selective, or none |
contextSplitting | boolean | true | Split contexts to prevent unnecessary re-renders |
Best Practices
-
Split contexts by update frequency — Keep authentication context separate from theme context. Components subscribing to one won't re-render when the other changes. Group state that changes together.
-
Use
useCallbackanduseMemointentionally — Only memoize values passed as props to memoized children or used in dependency arrays. Premature memoization adds complexity without measurable benefit. -
Colocate state with its consumers — Start with local state and lift only when siblings need it. Reach for context when prop drilling exceeds 2-3 levels. External stores suit cross-cutting global state.
-
Implement error boundaries at feature boundaries — A failing widget shouldn't crash the entire page. Place error boundaries around independent features so failures remain isolated.
-
Prefer composition over configuration — Build flexible components through children and slots rather than increasingly complex prop APIs. Compound components scale better than components with 20+ props.
Common Issues
Infinite re-render loops with useEffect — This happens when objects or arrays in the dependency array are recreated each render. Stabilize references with useMemo, extract values to primitives, or move object creation inside the effect. Never omit dependencies to "fix" the loop.
Stale closures in event handlers and timers — Callbacks inside useEffect or setTimeout capture the state value from when they were created. Use refs for values that need to be current inside long-lived closures, or use the functional form of setState (setCount(prev => prev + 1)).
Context causing unnecessary re-renders — When a context provider's value is an object literal created inline, every render creates a new reference and all consumers re-render. Memoize the context value with useMemo and split contexts so consumers only subscribe to the state slices they need.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.