react-patterns
React 19 specific patterns including React Compiler optimization, Server Actions, Forms, and new hooks. Use when implementing React 19 features, optimizing components, or choosing between Actions vs TanStack Query for mutations.
What this skill does
# React 19 Patterns and Best Practices
Modern React 19 patterns leveraging the React Compiler, Server Actions, and new hooks.
## Compiler-Friendly Code
The React Compiler automatically optimizes components for performance. Write code that works well with it:
**Best Practices:**
- Keep components pure and props serializable
- Derive values during render (don't stash in refs unnecessarily)
- Keep event handlers inline unless they close over large mutable objects
- Verify compiler is working (DevTools ✨ badge)
- Opt-out problematic components with `"use no memo"` while refactoring
**Example - Pure Component:**
```typescript
// ✅ Compiler-friendly - pure function
function UserCard({ user }: { user: User }) {
const displayName = `${user.firstName} ${user.lastName}`
const isVIP = user.points > 1000
return (
<div>
<h2>{displayName}</h2>
{isVIP && <Badge>VIP</Badge>}
</div>
)
}
// ❌ Avoid - unnecessary effects
function UserCard({ user }: { user: User }) {
const [displayName, setDisplayName] = useState('')
useEffect(() => {
setDisplayName(`${user.firstName} ${user.lastName}`)
}, [user])
return <div><h2>{displayName}</h2></div>
}
```
**Verification:**
- Open React DevTools
- Look for "Memo ✨" badge on components
- If missing, component wasn't optimized (check for violations)
**Opt-Out When Needed:**
```typescript
'use no memo'
// Component code that can't be optimized yet
function ProblematicComponent() {
// ... code with compiler issues
}
```
## Actions & Forms
For SPA mutations, choose **one approach per feature**:
- **React 19 Actions:** `<form action={fn}>`, `useActionState`, `useOptimistic`
- **TanStack Query:** `useMutation`
Don't duplicate logic between both approaches.
### React 19 Actions (Form-Centric)
**Best for:**
- Form submissions
- Simple CRUD operations
- When you want form validation built-in
**Basic Action:**
```typescript
'use server' // Only if using SSR/RSC, omit for SPA
async function createTodoAction(formData: FormData) {
const text = formData.get('text') as string
// Validation
if (!text || text.length < 3) {
return { error: 'Text must be at least 3 characters' }
}
// API call
await api.post('/todos', { text })
// Revalidation happens automatically
return { success: true }
}
// Component
function TodoForm() {
return (
<form action={createTodoAction}>
<input name="text" required />
<button type="submit">Add Todo</button>
</form>
)
}
```
**With State (useActionState):**
```typescript
import { useActionState } from 'react'
function TodoForm() {
const [state, formAction, isPending] = useActionState(
createTodoAction,
{ error: null, success: false }
)
return (
<form action={formAction}>
{state.error && <ErrorMessage>{state.error}</ErrorMessage>}
<input name="text" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Adding...' : 'Add Todo'}
</button>
</form>
)
}
```
**With Optimistic Updates (useOptimistic):**
```typescript
import { useOptimistic } from 'react'
function TodoList({ initialTodos }: { initialTodos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
initialTodos,
(state, newTodo: string) => [
...state,
{ id: `temp-${Date.now()}`, text: newTodo, completed: false }
]
)
async function handleSubmit(formData: FormData) {
const text = formData.get('text') as string
addOptimisticTodo(text)
await createTodoAction(formData)
}
return (
<>
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.id.startsWith('temp-') ? 0.5 : 1 }}>
{todo.text}
</li>
))}
</ul>
<form action={handleSubmit}>
<input name="text" required />
<button type="submit">Add</button>
</form>
</>
)
}
```
### TanStack Query Mutations (Preferred for SPAs)
**Best for:**
- Non-form mutations (e.g., button clicks)
- Complex optimistic updates with rollback
- Integration with existing Query cache
- More control over caching and invalidation
See **tanstack-query** skill for comprehensive mutation patterns.
**Quick Example:**
```typescript
import { useMutation, useQueryClient } from '@tanstack/react-query'
function useCre
ateTodo() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (text: string) => api.post('/todos', { text }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
}
// Usage
function TodoForm() {
const createTodo = useCreateTodo()
return (
<form onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
createTodo.mutate(formData.get('text') as string)
}}>
<input name="text" required />
<button type="submit" disabled={createTodo.isPending}>
{createTodo.isPending ? 'Adding...' : 'Add Todo'}
</button>
</form>
)
}
```
## The `use` Hook
The `use` hook unwraps Promises and Context, enabling new patterns.
**With Promises:**
```typescript
import { use, Suspense } from 'react'
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise)
return <div>{user.name}</div>
}
// Usage
function App() {
const userPromise = fetchUser(1)
return (
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
)
}
```
**With Context:**
```typescript
import { use, createContext } from 'react'
const ThemeContext = createContext<string>('light')
function Button() {
const theme = use(ThemeContext)
return <button className={theme}>Click me</button>
}
```
**When to Use:**
- Primarily useful with Suspense/data primitives and RSC (React Server Components)
- **For SPA-only apps**, prefer **TanStack Query + Router loaders** for data fetching
- `use` shines when you already have a Promise from a parent component
## Component Composition Patterns
**Compound Components:**
```typescript
// ✅ Good - composable, flexible
<Card>
<Card.Header>
<Card.Title>Dashboard</Card.Title>
</Card.Header>
<Card.Content>
{/* content */}
</Card.Content>
</Card>
// Implementation
function Card({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>
}
Card.Header = function CardHeader({ children }: { children: React.ReactNode }) {
return <header className="card-header">{children}</header>
}
Card.Title = function CardTitle({ children }: { children: React.ReactNode }) {
return <h2 className="card-title">{children}</h2>
}
Card.Content = function CardContent({ children }: { children: React.ReactNode }) {
return <div className="card-content">{children}</div>
}
```
**Render Props (when needed):**
```typescript
function DataLoader<T>({
fetch,
render
}: {
fetch: () => Promise<T>
render: (data: T) => React.ReactNode
}) {
const { data } = useQuery({ queryKey: ['data'], queryFn: fetch })
if (!data) return <Spinner />
return <>{render(data)}</>
}
// Usage
<DataLoader
fetch={() => fetchUser(1)}
render={(user) => <UserCard user={user} />}
/>
```
## Error Boundaries
React 19 still requires class components for error boundaries (or use a library):
```typescript
import { Component, ReactNode } from 'react'
class ErrorBoundary extends Component<
{ children: ReactNode; fallback: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false }
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(error: Error, info: { componentStack: string }) {
console.error('Error caught:', error, info)
}
render() {
if (this.state.hasError) {
return this.props.fallback
}
return this.props.children
}
}
// Usage
<ErrorBoundary fallback={<ErrorFallback />}>
<App />
</ErrorBoundary>
```
**Or use react-error-boundary library:**
```typescript
imporRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.