Agent Skills

react-patterns

Provides comprehensive React 19 patterns for Server Components, Server Actions, useOptimistic, useActionState, useTransition, concurrent features, Suspense boundaries, and TypeScript integration. Generates executable code patterns, validates security for public endpoints, and optimizes performance with React Compiler or manual memoization. Proactively use when building React 19 applications with Next.js App Router, implementing optimistic UI, or optimizing concurrent rendering.

Install

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill react-patterns
SKILL.md

React 19 Development Patterns

Overview

React 19 patterns for Next.js App Router, Server Actions, optimistic UI, and concurrent features. See Quick Reference for API summary and Examples for copy-paste patterns.

When to Use

  • Building React 19 applications with Next.js App Router
  • Implementing optimistic UI with useOptimistic or useTransition
  • Creating Server Actions with form validation
  • Migrating from class components to hooks
  • Optimizing concurrent rendering with React Compiler
  • Managing complex state with useReducer or custom hooks
  • Wrapping async operations in Suspense boundaries

Quick Reference

Pattern Hook / API Use Case
Local state useState Simple component state
Complex state useReducer Multi-action state machines
Side effects useEffect Subscriptions, data fetching
Shared state useContext / createContext Cross-component data
DOM access useRef Focus, measurements, timers
Performance useMemo / useCallback Expensive computations
Non-urgent updates useTransition Search/filter on large lists
Defer expensive UI useDeferredValue Stale-while-updating
Read resources use() (React 19) Promises and context in render
Optimistic UI useOptimistic (React 19) Instant feedback on mutations
Form status useFormStatus (React 19) Pending state in child components
Form state useActionState (React 19) Server action results
Auto-memoization React Compiler Eliminates manual memo/callback

Instructions

  1. Identify Component Type: Determine if Server Component or Client Component is needed
  2. Select Hooks: Use appropriate hooks for state management and side effects
  3. Type Props: Define TypeScript interfaces for all component props
  4. Handle Async: Wrap data-fetching components in Suspense boundaries
  5. Optimize: Use React Compiler or manual memoization for expensive renders
  6. Handle Errors: Add ErrorBoundary for graceful error handling
  7. Validate Server Actions: Define Zod/schema validation, then test:
    • Submit invalid inputs → verify rejection
    • Submit valid inputs → verify success

Examples

Server Component with Client Interaction

// Server Component (default) — async, fetches data
async function ProductPage({ id }: { id: string }) {
  const product = await db.product.findUnique({ where: { id } });

  return (
    <div>
      <h1>{product.name}</h1>
      <AddToCartButton productId={product.id} />
    </div>
  );
}

// Client Component — handles interactivity
'use client';
function AddToCartButton({ productId }: { productId: string }) {
  const [isPending, startTransition] = useTransition();

  const handleAdd = () => {
    startTransition(async () => {
      await addToCart(productId);
    });
  };

  return (
    <button onClick={handleAdd} disabled={isPending}>
      {isPending ? 'Adding...' : 'Add to Cart'}
    </button>
  );
}

useOptimistic for Instant Feedback

'use client';
import { useOptimistic } from 'react';

function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (t: Todo) => Promise<void> }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo: Todo) => [...state, { ...newTodo, pending: true }]
  );

  const handleSubmit = async (formData: FormData) => {
    const newTodo = { id: Date.now(), text: formData.get('text') as string };
    addOptimisticTodo(newTodo);  // Immediate UI update
    await addTodo(newTodo);      // Actual backend call
  };

  return (
    <form action={handleSubmit}>
      {optimisticTodos.map(todo => (
        <div key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
          {todo.text}
        </div>
      ))}
      <input type="text" name="text" />
      <button type="submit">Add</button>
    </form>
  );
}

Server Action with Form

// app/actions.ts
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';

const schema = z.object({
  title: z.string().min(5),
  content: z.string().min(10),
});

export async function createPost(prevState: any, formData: FormData) {
  const parsed = schema.safeParse({
    title: formData.get('title'),
    content: formData.get('content'),
  });

  if (!parsed.success) {
    return { errors: parsed.error.flatten().fieldErrors };
  }

  await db.post.create({ data: parsed.data });
  revalidatePath('/posts');
  return { success: true };
}

// app/blog/new/page.tsx
'use client';
import { useActionState } from 'react';
import { createPost } from '../actions';

export default function NewPostPage() {
  const [state, formAction, pending] = useActionState(createPost, {});

  return (
    <form action={formAction}>
      <input name="title" placeholder="Title" />
      {state.errors?.title && <span>{state.errors.title[0]}</span>}
      <textarea name="content" placeholder="Content" />
      <button type="submit" disabled={pending}>
        {pending ? 'Publishing...' : 'Publish'}
      </button>
    </form>
  );
}

Custom Hook

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;
}

useTransition for Non-Urgent Updates

function SearchableList({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  const [filteredItems, setFilteredItems] = useState(items);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setQuery(e.target.value);
    startTransition(() => {
      setFilteredItems(items.filter(i => i.name.toLowerCase().includes(e.target.value.toLowerCase())));
    });
  };

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending && <span>Filtering...</span>}
      <ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>
    </div>
  );
}

Best Practices

Server vs Client Decision

  • Start with Server Component (no directive needed)
  • Add 'use client' only for: hooks, browser APIs, event handlers

State Management

  • Keep state minimal — compute derived values during render, not in effects
  • Use useReducer for state with multiple related actions
  • Lift state up to the nearest common ancestor

Effects

  • Use effects only for external system synchronization
  • Always specify correct dependency arrays
  • Return cleanup functions for subscriptions and timers
  • Never mutate state directly — always create new references

Performance

  • With React Compiler: avoid manual useMemo, useCallback, memo
  • Without React Compiler: use useMemo for expensive computations, useCallback for stable callbacks
  • Use useTransition for low-priority state updates
  • Use stable IDs as list keys, not array indices

React 19 Specifics

  • Wrap use(promise) components in Suspense boundaries
  • Use useActionState for form-server action integration
  • Validate Server Action inputs — they are public endpoints
  • Pass serializable data from Server to Client Components

Constraints and Warnings

  • Server Components: Cannot use hooks, event handlers, or browser APIs
  • use() Hook: Can only be called during render, not in callbacks or effects
  • Server Actions: Must include 'use server' directive; always validate inputs
  • State Mutations: Never mutate state directly — always create new references
  • Effect Dependencies: Include all dependencies in useEffect dependency arrays
  • Memory Leaks: Always clean up subscriptions and event listeners in useEffect return

References

Consult these files for detailed patterns:

Related skills

vercel-react-best-practicesvercel-labs751KReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.vercel-composition-patternsvercel-labs361KReact composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.vercel-react-view-transitionsvercel-labs135KGuide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions viewpick-ui-libraryemilkowalski124KPick the right library for a given frontend task from a curated, opinionated list — numbers, OTP inputs, charts, command menus, virtualization, drag and drop, toasts, state, styling, and more. Only runs when explicitly invoked; it does not trigger on its own.

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers