react-patterns

Use when implementing React 19 features, optimizing components, or choosing between Actions vs TanStack Query for mutations. Covers React Compiler optimization, Server Actions, Forms, and new hooks.

248 stars

Best use case

react-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when implementing React 19 features, optimizing components, or choosing between Actions vs TanStack Query for mutations. Covers React Compiler optimization, Server Actions, Forms, and new hooks.

Teams using react-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/react-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/MadAppGang/claude-code/main/plugins/frontend/skills/react-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/react-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How react-patterns Compares

Feature / Agentreact-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when implementing React 19 features, optimizing components, or choosing between Actions vs TanStack Query for mutations. Covers React Compiler optimization, Server Actions, Forms, and new hooks.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# 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 TaskList({ 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
import { ErrorBoundary } from 'react-error-boundary'

<ErrorBoundary
  fallback={<div>Something went wrong</div>}
  onError={(error, info) => console.error(error, info)}
>
  <App />
</ErrorBoundary>
```

## Decision Guide: Actions vs Query Mutations

| Scenario | Recommendation |
|----------|---------------|
| Form submission with validation | React Actions |
| Button click mutation | TanStack Query |
| Needs optimistic updates + rollback | TanStack Query |
| Integrates with existing cache | TanStack Query |
| SSR/RSC application | React Actions |
| SPA with complex data flow | TanStack Query |
| Simple CRUD with forms | React Actions |

**Rule of Thumb:** For SPAs with TanStack Query already in use, prefer Query mutations for consistency. Only use Actions for form-heavy features where the form-centric API is beneficial.

## Related Skills

- **tanstack-query** - Server state with mutations and optimistic updates
- **core-principles** - Overall project structure
- **tooling-setup** - React Compiler configuration

Related Skills

data-extraction-patterns

248
from MadAppGang/claude-code

Common patterns for extracting analytics data from GA4 and GSC with API handling

batching-patterns

248
from MadAppGang/claude-code

Batch all related operations into single messages for maximum parallelism and performance. Use when launching multiple agents, reading multiple files, running parallel searches, optimizing workflow speed, or avoiding sequential execution bottlenecks. Trigger keywords - "batching", "parallel", "single message", "golden rule", "concurrent", "performance", "sequential bottleneck", "speed optimization".

ab-testing-patterns

248
from MadAppGang/claude-code

A/B testing methodology for cold email optimization

plugin-sdk-patterns

248
from MadAppGang/claude-code

Unified patterns and templates for creating consistent Claude Code plugins. Use when creating new plugins, designing plugin architecture, implementing builder patterns, or standardizing plugin structure. Trigger keywords - "plugin SDK", "plugin template", "plugin pattern", "builder pattern", "plugin structure", "new plugin", "plugin architecture".

react-typescript

248
from MadAppGang/claude-code

Modern React 19+ patterns with TypeScript including function components, hooks, state management, TanStack Query integration, form handling with Zod, error boundaries, and performance optimization. Use when building React applications, implementing components, or setting up state management.

universal-patterns

248
from MadAppGang/claude-code

Use when implementing language-agnostic patterns like layered architecture, dependency injection, error handling, or code organization principles across any technology stack.

database-patterns

248
from MadAppGang/claude-code

Use when designing database schemas, implementing repository patterns, writing optimized queries, managing migrations, or working with indexes and transactions for SQL/NoSQL databases.

auth-patterns

248
from MadAppGang/claude-code

Use when implementing authentication (JWT, sessions, OAuth), authorization (RBAC, ABAC), password hashing, MFA, or security best practices for backend services.

patterns

248
from MadAppGang/claude-code

Common agent patterns and templates for Claude Code. Use when implementing agents to follow proven patterns for Tasks integration, quality checks, and external model invocation via claudish CLI.

test-skill

248
from MadAppGang/claude-code

A test skill for validation testing. Use when testing skill parsing and validation logic.

bad-skill

248
from MadAppGang/claude-code

This skill has invalid YAML in frontmatter

release

248
from MadAppGang/claude-code

Plugin release process for MAG Claude Plugins marketplace. Covers version bumping, marketplace.json updates, git tagging, and common mistakes. Use when releasing new plugin versions or troubleshooting update issues.