--- name: react-patterns version: 2.0.0 description: Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern TypeScript development. Proactively use for any React development, component architecture, state management, performance optimization, or when implementing React 19's latest features. language: typescript,javascript,tsx,jsx framework: react license: MIT allowed-tools: Read, Write, Edit, Bash, Grep, Glob tags: [react, react-19, typescript, javascript, jsx, tsx, hooks, server-components, actions, suspense, concurrent-rendering, react-compiler, use-optimistic, form-actions] category: frontend subcategories: [components, hooks, state-management, performance, server-components, forms, testing] --- # React Development Patterns Expert guide for building modern React 19 applications with new concurrent features, Server Components, Actions, and advanced patterns. ## When to Use - Building React 19 components with TypeScript/JavaScript - Managing component state with useState and useReducer - Handling side effects with useEffect - Optimizing performance with useMemo and useCallback - Creating custom hooks for reusable logic - Implementing component composition patterns - Working with refs using useRef - Using React 19's new features (use(), useOptimistic, useFormStatus) - Implementing Server Components and Actions - Working with Suspense and concurrent rendering - Building forms with new form hooks ## Core Hooks Patterns ### useState - State Management Basic state declaration and updates: ```typescript import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( ); } ``` State with initializer function (expensive computation): ```typescript const [state, setState] = useState(() => { const initialState = computeExpensiveValue(); return initialState; }); ``` Multiple state variables: ```typescript function UserProfile() { const [name, setName] = useState(''); const [age, setAge] = useState(0); const [email, setEmail] = useState(''); return (
setName(e.target.value)} /> setAge(Number(e.target.value))} /> setEmail(e.target.value)} />
); } ``` ### useEffect - Side Effects Basic effect with cleanup: ```typescript import { useEffect } from 'react'; function ChatRoom({ roomId }: { roomId: string }) { useEffect(() => { const connection = createConnection(roomId); connection.connect(); // Cleanup function return () => { connection.disconnect(); }; }, [roomId]); // Dependency array return
Connected to {roomId}
; } ``` Effect with multiple dependencies: ```typescript function ChatRoom({ roomId, serverUrl }: { roomId: string; serverUrl: string }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [roomId, serverUrl]); // Re-run when either changes return

Welcome to {roomId}

; } ``` Effect for subscriptions: ```typescript function StatusBar() { const [isOnline, setIsOnline] = useState(true); useEffect(() => { function handleOnline() { setIsOnline(true); } function handleOffline() { setIsOnline(false); } window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); // Empty array = run once on mount return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; } ``` ### useRef - Persistent References Storing mutable values without re-renders: ```typescript import { useRef } from 'react'; function Timer() { const intervalRef = useRef(null); const startTimer = () => { intervalRef.current = setInterval(() => { console.log('Tick'); }, 1000); }; const stopTimer = () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; return ( <> ); } ``` DOM element references: ```typescript function TextInput() { const inputRef = useRef(null); const focusInput = () => { inputRef.current?.focus(); }; return ( <> ); } ``` ## Custom Hooks Pattern Extract reusable logic into custom hooks: ```typescript // useOnlineStatus.ts import { useState, useEffect } from 'react'; export function useOnlineStatus() { const [isOnline, setIsOnline] = useState(true); useEffect(() => { function handleOnline() { setIsOnline(true); } function handleOffline() { setIsOnline(false); } window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); return isOnline; } // Usage in components function StatusBar() { const isOnline = useOnlineStatus(); return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; } function SaveButton() { const isOnline = useOnlineStatus(); return ( ); } ``` Custom hook with parameters: ```typescript // useChatRoom.ts import { useEffect } from 'react'; interface ChatOptions { serverUrl: string; roomId: string; } export function useChatRoom({ serverUrl, roomId }: ChatOptions) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [serverUrl, roomId]); } // Usage function ChatRoom({ roomId }: { roomId: string }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useChatRoom({ serverUrl, roomId }); return ( <> setServerUrl(e.target.value)} />

Welcome to {roomId}

); } ``` ## Component Composition Patterns ### Props and Children Basic component with props: ```typescript interface ButtonProps { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; onClick?: () => void; children: React.ReactNode; } function Button({ variant = 'primary', size = 'md', onClick, children }: ButtonProps) { return ( ); } ``` Composition with children: ```typescript interface CardProps { children: React.ReactNode; className?: string; } function Card({ children, className = '' }: CardProps) { return (
{children}
); } // Usage function UserProfile() { return (

John Doe

Software Engineer

); } ``` ### Lifting State Up Shared state between siblings: ```typescript function Parent() { const [activeIndex, setActiveIndex] = useState(0); return ( <> setActiveIndex(0)} > Panel 1 content setActiveIndex(1)} > Panel 2 content ); } interface PanelProps { isActive: boolean; onShow: () => void; children: React.ReactNode; } function Panel({ isActive, onShow, children }: PanelProps) { return (
{isActive &&
{children}
}
); } ``` ## Performance Optimization ### Avoid Unnecessary Effects ❌ Bad - Using effect for derived state: ```typescript function TodoList({ todos }: { todos: Todo[] }) { const [visibleTodos, setVisibleTodos] = useState([]); useEffect(() => { setVisibleTodos(todos.filter(t => !t.completed)); }, [todos]); // Unnecessary effect return
    {/* ... */}
; } ``` ✅ Good - Compute during render: ```typescript function TodoList({ todos }: { todos: Todo[] }) { const visibleTodos = todos.filter(t => !t.completed); // Direct computation return
    {/* ... */}
; } ``` ### useMemo for Expensive Computations ```typescript import { useMemo } from 'react'; function DataTable({ data }: { data: Item[] }) { const sortedData = useMemo(() => { return [...data].sort((a, b) => a.name.localeCompare(b.name)); }, [data]); // Only recompute when data changes return {/* render sortedData */}
; } ``` ### useCallback for Function Stability ```typescript import { useCallback } from 'react'; function Parent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Clicked', count); }, [count]); // Recreate only when count changes return ; } ``` ## TypeScript Best Practices ### Type-Safe Props ```typescript interface UserProps { id: string; name: string; email: string; age?: number; // Optional } function User({ id, name, email, age }: UserProps) { return (

{name}

{email}

{age &&

Age: {age}

}
); } ``` ### Generic Components ```typescript interface ListProps { items: T[]; renderItem: (item: T) => React.ReactNode; } function List({ items, renderItem }: ListProps) { return (
    {items.map((item, index) => (
  • {renderItem(item)}
  • ))}
); } // Usage {user.name}} /> ``` ### Event Handlers ```typescript function Form() { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // Handle form submission }; const handleChange = (e: React.ChangeEvent) => { console.log(e.target.value); }; return (
); } ``` ## Common Patterns ### Controlled Components ```typescript function ControlledInput() { const [value, setValue] = useState(''); return ( setValue(e.target.value)} /> ); } ``` ### Conditional Rendering ```typescript function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) { return (
{isLoggedIn ? ( ) : ( )}
); } ``` ### Lists and Keys ```typescript function UserList({ users }: { users: User[] }) { return (
    {users.map(user => (
  • {user.name}
  • ))}
); } ``` ## Best Practices ### General React Best Practices 1. **Dependency Arrays**: Always specify correct dependencies in useEffect 2. **State Structure**: Keep state minimal and avoid redundant state 3. **Component Size**: Keep components small and focused 4. **Custom Hooks**: Extract complex logic into reusable custom hooks 5. **TypeScript**: Use TypeScript for type safety 6. **Keys**: Use stable IDs as keys for list items, not array indices 7. **Immutability**: Never mutate state directly 8. **Effects**: Use effects only for synchronization with external systems 9. **Performance**: Profile before optimizing with useMemo/useCallback ### React 19 Specific Best Practices 1. **Server Components**: Use Server Components for data fetching and static content 2. **Client Components**: Mark components as 'use client' only when necessary 3. **Actions**: Use Server Actions for mutations and form submissions 4. **Optimistic Updates**: Implement useOptimistic for better UX 5. **use() Hook**: Use for reading promises and context conditionally 6. **Form State**: Use useFormState and useFormStatus for complex forms 7. **Concurrent Features**: Leverage useTransition for non-urgent updates 8. **Error Boundaries**: Implement proper error handling with error boundaries ## Common Pitfalls ### General React Pitfalls ❌ **Missing Dependencies**: ```typescript useEffect(() => { // Uses 'count' but doesn't include it in deps console.log(count); }, []); // Wrong! ``` ❌ **Mutating State**: ```typescript const [items, setItems] = useState([]); items.push(newItem); // Wrong! Mutates state setItems(items); // Won't trigger re-render ``` ✅ **Correct Approach**: ```typescript setItems([...items, newItem]); // Create new array ``` ### React 19 Specific Pitfalls ❌ **Using use() outside of render**: ```typescript // Wrong! function handleClick() { const data = use(promise); // Error: use() can only be called in render } ``` ✅ **Correct usage**: ```tsx function Component({ promise }) { const data = use(promise); // Correct: called during render return
{data}
; } ``` ❌ **Forgetting 'use server' directive**: ```typescript // Wrong - missing 'use server' export async function myAction() { // This will run on the client! } ``` ✅ **Correct Server Action**: ```typescript 'use server'; // Must be at the top export async function myAction() { // Now runs on the server } ``` ❌ **Mixing Server and Client logic incorrectly**: ```tsx // Wrong - trying to use browser APIs in Server Component export default async function ServerComponent() { const width = window.innerWidth; // Error: window is not defined return
{width}
; } ``` ✅ **Correct separation**: ```tsx // Server Component for data export default async function ServerComponent() { const data = await fetchData(); return ; } // Client Component for browser APIs 'use client'; function ClientComponent({ data }) { const [width, setWidth] = useState(window.innerWidth); // Handle resize logic... return
{width}
; } ``` ## React 19 New Features ### use() Hook - Reading Resources The `use()` hook reads the value from a resource like a Promise or Context: ```typescript import { use } from 'react'; // Reading a Promise in a component function MessageComponent({ messagePromise }) { const message = use(messagePromise); return

{message}

; } // Reading Context conditionally function Button() { if (condition) { const theme = use(ThemeContext); return ; } return ; } ``` ### useOptimistic Hook - Optimistic UI Updates Manage optimistic UI updates for async operations: ```typescript import { useOptimistic } from 'react'; function TodoList({ todos, addTodo }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [...state, newTodo] ); const handleSubmit = async (formData) => { const newTodo = { id: Date.now(), text: formData.get('text') }; // Optimistically add to UI addOptimisticTodo(newTodo); // Actually add to backend await addTodo(newTodo); }; return (
{optimisticTodos.map(todo => (
{todo.text}
))}
); } ``` ### useFormStatus Hook - Form State Access form submission status from child components: ```typescript import { useFormStatus } from 'react'; function SubmitButton() { const { pending, data } = useFormStatus(); return ( ); } function ContactForm() { return (
); } ``` ### useFormState Hook - Form State Management Manage form state with error handling: ```typescript import { useFormState } from 'react'; async function submitAction(prevState: string | null, formData: FormData) { const email = formData.get('email') as string; if (!email.includes('@')) { return 'Invalid email address'; } await submitToDatabase(email); return null; } function EmailForm() { const [state, formAction] = useFormState(submitAction, null); return (
{state &&

{state}

}
); } ``` ### Server Actions Define server-side functions for form handling: ```typescript // app/actions.ts 'use server'; import { redirect } from 'next/navigation'; import { revalidatePath } from 'next/cache'; export async function createPost(formData: FormData) { const title = formData.get('title') as string; const content = formData.get('content') as string; // Validate input if (!title || !content) { return { error: 'Title and content are required' }; } // Save to database const post = await db.post.create({ data: { title, content } }); // Update cache and redirect revalidatePath('/posts'); redirect(`/posts/${post.id}`); } ``` ### Server Components Components that run exclusively on the server: ```typescript // app/posts/page.tsx - Server Component async function PostsPage() { // Server-side data fetching const posts = await db.post.findMany({ orderBy: { createdAt: 'desc' }, take: 10 }); return (

Latest Posts

); } // Client Component for interactivity 'use client'; function PostsList({ posts }: { posts: Post[] }) { const [selectedId, setSelectedId] = useState(null); return (
    {posts.map(post => (
  • setSelectedId(post.id)} className={selectedId === post.id ? 'selected' : ''} > {post.title}
  • ))}
); } ``` ## React Compiler ### Automatic Optimization React Compiler automatically optimizes your components: ```typescript // Before React Compiler - manual memoization needed const ExpensiveComponent = memo(function ExpensiveComponent({ data, onUpdate }) { const processedData = useMemo(() => { return data.map(item => ({ ...item, computed: expensiveCalculation(item) })); }, [data]); const handleClick = useCallback((id) => { onUpdate(id); }, [onUpdate]); return (
{processedData.map(item => ( ))}
); }); // After React Compiler - no manual optimization needed function ExpensiveComponent({ data, onUpdate }) { const processedData = data.map(item => ({ ...item, computed: expensiveCalculation(item) })); const handleClick = (id) => { onUpdate(id); }; return (
{processedData.map(item => ( ))}
); } ``` ### Installation and Setup ```bash # Install React Compiler npm install -D babel-plugin-react-compiler@latest # Install ESLint plugin for validation npm install -D eslint-plugin-react-hooks@latest ``` ```javascript // babel.config.js module.exports = { plugins: [ 'babel-plugin-react-compiler', // Must run first! // ... other plugins ], }; ``` ```javascript // vite.config.js for Vite users import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ react({ babel: { plugins: ['babel-plugin-react-compiler'], }, }), ], }); ``` ### Compiler Configuration ```javascript // babel.config.js with compiler options module.exports = { plugins: [ [ 'babel-plugin-react-compiler', { // Enable compilation for specific files target: '18', // or '19' // Debug mode for development debug: process.env.NODE_ENV === 'development' } ], ], }; // Incremental adoption with overrides module.exports = { plugins: [], overrides: [ { test: './src/components/**/*.{js,jsx,ts,tsx}', plugins: ['babel-plugin-react-compiler'] } ] }; ``` ## Advanced Server Components Patterns ### Mixed Server/Client Architecture ```typescript // Server Component for data fetching async function ProductPage({ id }: { id: string }) { const product = await fetchProduct(id); const related = await fetchRelatedProducts(id); return (
); } // Client Component for interactivity 'use client'; function ProductDetails({ product }: { product: Product }) { const [quantity, setQuantity] = useState(1); const [isAdded, setIsAdded] = useState(false); return (

{product.name}

{product.description}

${product.price}

setIsAdded(true)} /> {isAdded &&

Added to cart!

}
); } ``` ### Server Actions with Validation ```typescript 'use server'; import { z } from 'zod'; const checkoutSchema = z.object({ items: z.array(z.object({ productId: z.string(), quantity: z.number().min(1) })), shippingAddress: z.object({ street: z.string().min(1), city: z.string().min(1), zipCode: z.string().regex(/^\d{5}$/) }), paymentMethod: z.enum(['credit', 'paypal', 'apple']) }); export async function processCheckout( prevState: any, formData: FormData ) { // Extract and validate data const rawData = { items: JSON.parse(formData.get('items') as string), shippingAddress: { street: formData.get('street'), city: formData.get('city'), zipCode: formData.get('zipCode') }, paymentMethod: formData.get('paymentMethod') }; const result = checkoutSchema.safeParse(rawData); if (!result.success) { return { error: 'Validation failed', fieldErrors: result.error.flatten().fieldErrors }; } try { // Process payment const order = await createOrder(result.data); // Update inventory await updateInventory(result.data.items); // Send confirmation await sendConfirmationEmail(order); // Revalidate cache revalidatePath('/orders'); return { success: true, orderId: order.id }; } catch (error) { return { error: 'Payment failed' }; } } ``` ## Concurrent Features ### useTransition for Non-Urgent Updates ```typescript import { useTransition, useState } from 'react'; function SearchableList({ items }: { items: Item[] }) { const [query, setQuery] = useState(''); const [isPending, startTransition] = useTransition(); const [filteredItems, setFilteredItems] = useState(items); const handleChange = (e: React.ChangeEvent) => { // Update input immediately setQuery(e.target.value); // Transition the filter operation startTransition(() => { setFilteredItems( items.filter(item => item.name.toLowerCase().includes(e.target.value.toLowerCase()) ) ); }); }; return (
{isPending &&
Filtering...
}
    {filteredItems.map(item => (
  • {item.name}
  • ))}
); } ``` ### useDeferredValue for Expensive UI ```typescript import { useDeferredValue, useMemo } from 'react'; function DataGrid({ data }: { data: DataRow[] }) { const [searchTerm, setSearchTerm] = useState(''); const deferredSearchTerm = useDeferredValue(searchTerm); const filteredData = useMemo(() => { return data.filter(row => Object.values(row).some(value => String(value).toLowerCase().includes(deferredSearchTerm.toLowerCase()) ) ); }, [data, deferredSearchTerm]); return (
setSearchTerm(e.target.value)} placeholder="Search..." className={searchTerm !== deferredSearchTerm ? 'stale' : ''} />
); } ``` ## Testing React 19 Features ### Testing Server Actions ```typescript import { render, screen, fireEvent } from '@testing-library/react'; import { jest } from '@jest/globals'; import ContactForm from './ContactForm'; // Mock server action const mockSubmitForm = jest.fn(); describe('ContactForm', () => { it('submits form with server action', async () => { render(); fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'test@example.com' } }); fireEvent.click(screen.getByText('Submit')); expect(mockSubmitForm).toHaveBeenCalledWith( expect.any(FormData) ); }); it('shows loading state during submission', async () => { mockSubmitForm.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100))); render(); fireEvent.click(screen.getByText('Submit')); expect(screen.getByText('Submitting...')).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText('Submit')).toBeInTheDocument(); }); }); }); ``` ### Testing Optimistic Updates ```typescript import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { jest } from '@jest/globals'; import TodoList from './TodoList'; describe('useOptimistic', () => { it('shows optimistic update immediately', async () => { const mockAddTodo = jest.fn(() => new Promise(resolve => setTimeout(resolve, 100))); render( ); fireEvent.change(screen.getByPlaceholderText('Add a todo'), { target: { value: 'New todo' } }); fireEvent.click(screen.getByText('Add')); // Optimistic update appears immediately expect(screen.getByText('New todo')).toBeInTheDocument(); // Wait for actual submission await waitFor(() => { expect(mockAddTodo).toHaveBeenCalledWith({ id: expect.any(Number), text: 'New todo' }); }); }); }); ``` ## Performance Best Practices ### React Compiler Guidelines 1. **Write Standard React Code**: The compiler works best with idiomatic React patterns 2. **Avoid Manual Memoization**: Let the compiler handle useMemo, useCallback, and memo 3. **Keep Components Pure**: Avoid side effects in render 4. **Use Stable References**: Pass stable objects as props ```typescript // Good: Clean, idiomatic React function ProductCard({ product, onAddToCart }) { const [quantity, setQuantity] = useState(1); const handleAdd = () => { onAddToCart(product.id, quantity); }; return (

{product.name}

${product.price}

setQuantity(Number(e.target.value))} min="1" />
); } // Avoid: Manual optimization function ProductCard({ product, onAddToCart }) { const [quantity, setQuantity] = useState(1); const handleAdd = useCallback(() => { onAddToCart(product.id, quantity); }, [product.id, quantity, onAddToCart]); return (

{product.name}

${product.price}

); } ``` ### Server Components Best Practices 1. **Keep Server Components Server-Only**: No event handlers, hooks, or browser APIs 2. **Minimize Client Components**: Only use 'use client' when necessary 3. **Pass Data as Props**: Serialize data when passing from Server to Client 4. **Use Server Actions for Mutations**: Keep data operations on the server ```typescript // Good: Server Component for static content async function ProductPage({ id }: { id: string }) { const product = await fetchProduct(id); return (

{product.name}

{product.description}

{product.name}
); } // Client Component only for interactivity 'use client'; function AddToCartForm({ productId }: { productId: string }) { const [isAdding, setIsAdding] = useState(false); async function handleSubmit() { setIsAdding(true); await addToCart(productId); setIsAdding(false); } return (
); } ``` ## Migration Guide ### From React 18 to 19 1. **Update Dependencies**: ```bash npm install react@19 react-dom@19 ``` 2. **Adopt Server Components**: - Identify data-fetching components - Remove client-side code from Server Components - Add 'use client' directive where needed 3. **Replace Manual Optimistic Updates**: ```typescript // Before function TodoList({ todos, addTodo }) { const [optimisticTodos, setOptimisticTodos] = useState(todos); const handleAdd = async (text) => { const newTodo = { id: Date.now(), text }; setOptimisticTodos([...optimisticTodos, newTodo]); await addTodo(newTodo); }; } // After function TodoList({ todos, addTodo }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [...state, newTodo] ); const handleAdd = async (formData) => { const newTodo = { id: Date.now(), text: formData.get('text') }; addOptimisticTodo(newTodo); await addTodo(newTodo); }; } ``` 4. **Enable React Compiler**: - Install babel-plugin-react-compiler - Remove manual memoization - Let the compiler optimize automatically ## References - React 19 Official Docs: https://react.dev/blog/2024/04/25/react-19 - React Server Components: https://react.dev/reference/rsc/server-components - React Compiler: https://react.dev/learn/react-compiler - TypeScript with React: https://react-typescript-cheatsheet.netlify.app/