Generated banner for React19 Concurrent Patterns
Generated logo for React19 Concurrent Patterns

React19 Concurrent Patterns

GitHubOfficialFirst committed by SaravananRajaramanDistributed by GitHub

  • Skill
  • Guidelines
  • Software development
  • MIT
  • No issues found

From the author

Preserve React 18 concurrent patterns and adopt React 19 APIs (useTransition, useDeferredValue, Suspense, use(), useOptimistic, Actions) during migration.
— the description by SaravananRajaraman

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to migrating React applications to version 19. It emphasizes a two-phase strategy: first, ensuring that existing React 18 concurrent patterns remain intact and functional during the initial migration, and second, introducing new React 19 features like Actions, the use() hook, and improved Suspense patterns only after the migration is stabilized. It includes specific verification steps to prevent accidental breakage of concurrent logic.

What it does

  • Validates that existing React 18 concurrent patterns are preserved during migration.
  • Provides guidance on when to introduce new React 19 APIs.
  • Offers patterns for using React 19 Actions with useActionState and useFormStatus.
  • Demonstrates how to implement Suspense for data fetching using the use() hook.
  • Includes command-line verification to check for accidental modification of concurrent patterns.
  • Distinguishes between code-splitting and data-fetching use cases for Suspense.

When to use it

  • When planning or executing a migration from React 18 to React 19.
  • When you need to ensure concurrent mode features remain stable during a framework upgrade.
  • When you are ready to refactor legacy form handling into the new React 19 Actions pattern.
  • When adopting new data-fetching patterns using Suspense and the use() hook.

Advantages

  • Reduces the risk of breaking concurrent features during the migration process.
  • Provides a clear separation between migration stability and feature adoption.
  • Simplifies form handling by replacing complex useReducer logic with Actions.
  • Offers standardized patterns for modern data fetching and state management.

Usage examples

“How do I verify that my concurrent patterns were not broken during the React 19 migration?”
The agent provides a grep command to scan the codebase for key concurrent patterns like useTransition and useDeferredValue to ensure they remain untouched.
“Show me how to replace my React 18 form logic with React 19 Actions.”
The agent provides a side-by-side comparison showing the transition from useReducer to the useActionState hook.
“How can I use Suspense for data fetching in React 19?”
The agent explains the use() hook and provides patterns for memoizing promises to prevent unnecessary re-renders.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/github/react19-concurrent-patterns/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip react19-concurrent-patterns.zip -d .agents/skills

Documentation by SaravananRajaraman

React 19 Concurrent Patterns

React 19 introduced new APIs that complement the migration work. This skill covers two concerns:

  1. Preserve existing React 18 concurrent patterns that must not be broken during migration

  2. Adopt new React 19 APIs worth introducing after migration stabilizes

Part 1 Preserve: React 18 Concurrent Patterns That Must Survive the Migration

These patterns exist in React 18 codebases and must not be accidentally removed or broken:

createRoot Already Migrated by the R18 Orchestra

If the R18 orchestra already ran, ReactDOM.render → createRoot is done. Verify it's correct:

// CORRECT React 19 root (same as React 18):
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
useTransition No Migration Needed

useTransition from React 18 works identically in React 19. Do not touch these patterns during migration:

// React 18 useTransition  unchanged in React 19:
const [isPending, startTransition] = useTransition();

function handleClick() {
  startTransition(() => {
    setFilteredResults(computeExpensiveFilter(input));
  });
}
useDeferredValue No Migration Needed
// React 18 useDeferredValue  unchanged in React 19:
const deferredQuery = useDeferredValue(query);
Suspense for Code Splitting No Migration Needed
// React 18 Suspense with lazy  unchanged in React 19:
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <LazyComponent />
    </Suspense>
  );
}

Part 2 React 19 New APIs

These are worth adopting in a post-migration cleanup sprint. Do not introduce these DURING the migration stabilize first.

For full patterns on each new API, read:

  • references/react19-use.md the use() hook for promises and context

  • references/react19-actions.md Actions, useActionState, useFormStatus, useOptimistic

  • references/react19-suspense.md Suspense for data fetching (the new pattern)

Migration Safety Rules

During the React 19 migration itself, these concurrent-mode patterns must be left completely untouched:

# Verify nothing touched these during migration:
grep -rn "useTransition\|useDeferredValue\|Suspense\|startTransition" \
  src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."

If the migrator touched any of these files, review the changes the migration should only have modified React API surface (forwardRef, defaultProps, etc.), never concurrent mode logic.

Files

references/react19-suspense.md


title: React 19 Suspense for Data Fetching Pattern Reference

React 19 Suspense for Data Fetching Pattern Reference

React 19's new Suspense integration for data fetching is a preview feature that allows components to suspend (pause rendering) until data is available, without useEffect + state.

Important: This is a preview it requires specific setup and is not yet stable for production, but you should know the pattern for React 19 migration planning.


What Changed in React 19?

React 18 Suspense only supported code splitting (lazy components). React 19 extends it to data fetching if certain conditions are met:

  • Lib usage the data fetching library must implement Suspense (e.g., React Query 5+, SWR, Remix loaders)

  • Or your own promise tracking wrap promises in a way React can track their suspension

  • No more "no hook after suspense" you can use Suspense directly in components with use()


React 18 Suspense (Code Splitting Only)

// React 18  Suspense for lazy imports only:
const LazyComponent = React.lazy(() => import('./Component'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <LazyComponent />
    </Suspense>
  );
}

Trying to suspend for data in React 18 required hacks or libraries:

// React 18 hack  not recommended:
const dataPromise = fetchData();
const resource = {
  read: () => {
    throw dataPromise; // Throw to suspend
  }
};

function Component() {
  const data = resource.read(); // Throws promise → Suspense catches it
  return <div>{data}</div>;
}

React 19 Suspense for Data Fetching (Preview)

React 19 provides first-class support for Suspense with promises via the use() hook:

// React 19  Suspense for data fetching:
function UserProfile({ userId }) {
  const user = use(fetchUser(userId)); // Suspends if promise pending
  return <div>{user.name}</div>;
}

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <UserProfile userId={123} />
    </Suspense>
  );
}

Key differences from React 18:

  • use() unwraps the promise, component suspends automatically

  • No need for useEffect + state trick

  • Cleaner code, less boilerplate


Pattern 1: Simple Promise Suspense

// Raw promise (not recommended in production):
function DataComponent() {
  const data = use(fetch('/api/data').then(r => r.json()));
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <DataComponent />
    </Suspense>
  );
}

Problem: Promise is recreated every render. Solution: wrap in useMemo.


Pattern 2: Memoized Promise (Better)

function DataComponent({ id }) {
  // Only create promise once per id:
  const dataPromise = useMemo(() => 
    fetch(`/api/data/${id}`).then(r => r.json()),
    [id]
  );
  
  const data = use(dataPromise);
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

function App() {
  const [id, setId] = useState(1);
  
  return (
    <Suspense fallback={<Spinner />}>
      <DataComponent id={id} />
      <button onClick={() => setId(id + 1)}>Next</button>
    </Suspense>
  );
}

Pattern 3: Library Integration (React Query)

Modern data libraries support Suspense directly. React Query 5+ example:

// React Query 5+ with Suspense:
import { useSuspenseQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  // useSuspenseQuery throws promise if suspended
  const { data: user } = useSuspenseQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });
  
  return <div>{user.name}</div>;
}

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <UserProfile userId={123} />
    </Suspense>
  );
}

Advantage: Library handles caching, retries, and cache invalidation.


Pattern 4: Error Boundary Integration

Combine Suspense with Error Boundary to handle both loading and errors:

function UserProfile({ userId }) {
  const user = use(fetchUser(userId)); // Suspends while loading
  return <div>{user.name}</div>;
}

function App() {
  return (
    <ErrorBoundary fallback={<ErrorScreen />}>
      <Suspense fallback={<Spinner />}>
        <UserProfile userId={123} />
      </Suspense>
    </ErrorBoundary>
  );
}

class ErrorBoundary extends React.Component {
  state = { error: null };
  
  static getDerivedStateFromError(error) {
    return { error };
  }
  
  render() {
    if (this.state.error) return this.props.fallback;
    return this.props.children;
  }
}

Nested Suspense Boundaries

Use multiple Suspense boundaries to show partial UI while waiting for different data:

function App({ userId }) {
  return (
    <div>
      <Suspense fallback={<UserSpinner />}>
        <UserProfile userId={userId} />
      </Suspense>
      
      <Suspense fallback={<PostsSpinner />}>
        <UserPosts userId={userId} />
      </Suspense>
    </div>
  );
}

function UserProfile({ userId }) {
  const user = use(fetchUser(userId));
  return <h1>{user.name}</h1>;
}

function UserPosts({ userId }) {
  const posts = use(fetchUserPosts(userId));
  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}

Now:

  • User profile shows spinner while loading

  • Posts show spinner independently

  • Both can render as they complete


Sequential vs Parallel Suspense

Sequential (wait for first before fetching second)
function App({ userId }) {
  const user = use(fetchUser(userId)); // Must complete first
  
  return (
    <Suspense fallback={<PostsSpinner />}>
      <UserPosts userId={user.id} /> {/* Depends on user */}
    </Suspense>
  );
}

function UserPosts({ userId }) {
  const posts = use(fetchUserPosts(userId));
  return <ul>{posts.map(p => <li>{p.title}</li>)}</ul>;
}
Parallel (fetch both at once)
function App({ userId }) {
  return (
    <div>
      <Suspense fallback={<UserSpinner />}>
        <UserProfile userId={userId} />
      </Suspense>
      
      <Suspense fallback={<PostsSpinner />}>
        <UserPosts userId={userId} /> {/* Fetches in parallel */}
      </Suspense>
    </div>
  );
}

Migration Strategy for React 18 → React 19

Phase 1 No changes required

Suspense is still optional and experimental for data fetching. All existing useEffect + state patterns continue to work.

Phase 2 Wait for stability

Before adopting Suspense data fetching in production:

  • Wait for React 19 to ship (not preview)

  • Verify your data library supports Suspense

  • Plan migration after app stabilizes on React 19 core

Phase 3 Refactor to Suspense (optional, post-preview)

Once stable, profile candidates:

grep -rn "useEffect.*fetch\|useEffect.*axios\|useEffect.*graphql" src/ --include="*.js" --include="*.jsx"
// Before (React 18):
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);
  
  if (!user) return <Spinner />;
  return <div>{user.name}</div>;
}

// After (React 19 with Suspense):
function UserProfile({ userId }) {
  const user = use(fetchUser(userId));
  return <div>{user.name}</div>;
}

// Must be wrapped in Suspense:
<Suspense fallback={<Spinner />}>
  <UserProfile userId={123} />
</Suspense>

Important Warnings

  1. Still Preview Suspense for data is marked experimental, behavior may change

  2. Performance promises are recreated on every render without memoization; use useMemo

  3. Cache use() doesn't cache; use React Query or similar for production apps

  4. SSR Suspense SSR support is limited; check Next.js version requirements

Requirements and compatibility

Written for: GitHub Copilot

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

First committed by
GitHub avatar of SaravananRajaramanSaravananRajaraman
Publisher
GitHub (Official)
Distributed by
GitHub
Source
github/awesome-copilot · skills/react19-concurrent-patterns · commit 797337f
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Contributors
SaravananRajaraman, stbenjam

AutoGineer is not affiliated with or endorsed by SaravananRajaraman or GitHub.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download react19-concurrent-patterns.zip