{"kind":"Skill","metadata":{"namespace":"community","name":"react19-concurrent-patterns","version":"0.1.0"},"spec":{"description":"Preserve React 18 concurrent patterns and adopt React 19 APIs (useTransition, useDeferredValue, Suspense, use(), useOptimistic, Actions) during migration.","files":{"SKILL.md":"---\nname: react19-concurrent-patterns\ndescription: 'Preserve React 18 concurrent patterns and adopt React 19 APIs (useTransition, useDeferredValue, Suspense, use(), useOptimistic, Actions) during migration.'\n---\n\n# React 19 Concurrent Patterns\n\nReact 19 introduced new APIs that complement the migration work. This skill covers two concerns:\n\n1. **Preserve**  existing React 18 concurrent patterns that must not be broken during migration\n2. **Adopt**  new React 19 APIs worth introducing after migration stabilizes\n\n## Part 1  Preserve: React 18 Concurrent Patterns That Must Survive the Migration\n\nThese patterns exist in React 18 codebases and must not be accidentally removed or broken:\n\n### createRoot  Already Migrated by the R18 Orchestra\n\nIf the R18 orchestra already ran, `ReactDOM.render` → `createRoot` is done. Verify it's correct:\n\n```jsx\n// CORRECT React 19 root (same as React 18):\nimport { createRoot } from 'react-dom/client';\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  \u003cReact.StrictMode\u003e\n    \u003cApp /\u003e\n  \u003c/React.StrictMode\u003e\n);\n```\n\n### useTransition  No Migration Needed\n\n`useTransition` from React 18 works identically in React 19. Do not touch these patterns during migration:\n\n```jsx\n// React 18 useTransition  unchanged in React 19:\nconst [isPending, startTransition] = useTransition();\n\nfunction handleClick() {\n  startTransition(() =\u003e {\n    setFilteredResults(computeExpensiveFilter(input));\n  });\n}\n```\n\n### useDeferredValue  No Migration Needed\n\n```jsx\n// React 18 useDeferredValue  unchanged in React 19:\nconst deferredQuery = useDeferredValue(query);\n```\n\n### Suspense for Code Splitting  No Migration Needed\n\n```jsx\n// React 18 Suspense with lazy  unchanged in React 19:\nconst LazyComponent = React.lazy(() =\u003e import('./LazyComponent'));\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cLazyComponent /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n---\n\n## Part 2  React 19 New APIs\n\nThese are worth adopting in a post-migration cleanup sprint. Do not introduce these DURING the migration  stabilize first.\n\nFor full patterns on each new API, read:\n- **`references/react19-use.md`**  the `use()` hook for promises and context\n- **`references/react19-actions.md`**  Actions, useActionState, useFormStatus, useOptimistic\n- **`references/react19-suspense.md`**  Suspense for data fetching (the new pattern)\n\n## Migration Safety Rules\n\nDuring the React 19 migration itself, these concurrent-mode patterns must be **left completely untouched**:\n\n```bash\n# Verify nothing touched these during migration:\ngrep -rn \"useTransition\\|useDeferredValue\\|Suspense\\|startTransition\" \\\n  src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\"\n```\n\nIf 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.\n","references/react19-actions.md":"---\ntitle: React 19 Actions Pattern Reference\n---\n\n# React 19 Actions Pattern Reference\n\nReact 19 introduces **Actions**  a pattern for handling async operations (like form submissions) with built-in loading states, error handling, and optimistic updates. This replaces the `useReducer + state` pattern with a simpler API.\n\n## What are Actions?\n\nAn **Action** is an async function that:\n\n- Can be called automatically when a form submits or button clicks\n- Runs with automatic loading/pending state\n- Updates the UI automatically when done\n- Works with Server Components for direct server mutation\n\n---\n\n## useActionState()\n\n`useActionState` is the client-side Action hook. It replaces `useReducer + useEffect` for form handling.\n\n### React 18 Pattern\n\n```jsx\n// React 18  form with useReducer + state:\nfunction Form() {\n  const [state, dispatch] = useReducer(\n    (state, action) =\u003e {\n      switch (action.type) {\n        case 'loading':\n          return { ...state, loading: true, error: null };\n        case 'success':\n          return { ...state, loading: false, data: action.data };\n        case 'error':\n          return { ...state, loading: false, error: action.error };\n      }\n    },\n    { loading: false, data: null, error: null }\n  );\n  \n  async function handleSubmit(e) {\n    e.preventDefault();\n    dispatch({ type: 'loading' });\n    try {\n      const result = await submitForm(new FormData(e.target));\n      dispatch({ type: 'success', data: result });\n    } catch (err) {\n      dispatch({ type: 'error', error: err.message });\n    }\n  }\n  \n  return (\n    \u003cform onSubmit={handleSubmit}\u003e\n      \u003cinput name=\"email\" /\u003e\n      {state.loading \u0026\u0026 \u003cSpinner /\u003e}\n      {state.error \u0026\u0026 \u003cError msg={state.error} /\u003e}\n      {state.data \u0026\u0026 \u003cSuccess data={state.data} /\u003e}\n      \u003cbutton disabled={state.loading}\u003eSubmit\u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\n### React 19 useActionState() Pattern\n\n```jsx\n// React 19  same form with useActionState:\nimport { useActionState } from 'react';\n\nasync function submitFormAction(prevState, formData) {\n  // prevState = previous return value from this function\n  // formData = FormData from \u003cform action={submitFormAction}\u003e\n  \n  try {\n    const result = await submitForm(formData);\n    return { data: result, error: null };\n  } catch (err) {\n    return { data: null, error: err.message };\n  }\n}\n\nfunction Form() {\n  const [state, formAction, isPending] = useActionState(\n    submitFormAction,\n    { data: null, error: null } // initial state\n  );\n  \n  return (\n    \u003cform action={formAction}\u003e\n      \u003cinput name=\"email\" /\u003e\n      {isPending \u0026\u0026 \u003cSpinner /\u003e}\n      {state.error \u0026\u0026 \u003cError msg={state.error} /\u003e}\n      {state.data \u0026\u0026 \u003cSuccess data={state.data} /\u003e}\n      \u003cbutton disabled={isPending}\u003eSubmit\u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\n**Differences:**\n\n- One hook instead of `useReducer` + logic\n- `formAction` replaces `onSubmit`, form automatically collects FormData\n- `isPending` is a boolean, no dispatch calls\n- Action function receives `(prevState, formData)`\n\n---\n\n## useFormStatus()\n\n`useFormStatus` is a **child component hook** that reads the pending state from the nearest form. It acts like a built-in `isPending` signal without prop drilling.\n\n```jsx\n// React 18  must pass isPending as prop:\nfunction SubmitButton({ isPending }) {\n  return \u003cbutton disabled={isPending}\u003eSubmit\u003c/button\u003e;\n}\n\nfunction Form({ isPending, formAction }) {\n  return (\n    \u003cform action={formAction}\u003e\n      \u003cinput /\u003e\n      \u003cSubmitButton isPending={isPending} /\u003e\n    \u003c/form\u003e\n  );\n}\n\n// React 19  useFormStatus reads it automatically:\nfunction SubmitButton() {\n  const { pending } = useFormStatus();\n  return \u003cbutton disabled={pending}\u003eSubmit\u003c/button\u003e;\n}\n\nfunction Form() {\n  const [state, formAction] = useActionState(submitFormAction, {});\n  \n  return (\n    \u003cform action={formAction}\u003e\n      \u003cinput /\u003e\n      \u003cSubmitButton /\u003e {/* No prop needed */}\n    \u003c/form\u003e\n  );\n}\n```\n\n**Key point:** `useFormStatus` only works inside a `\u003cform action={...}\u003e`  regular `\u003cform onSubmit\u003e` won't trigger it.\n\n---\n\n## useOptimistic()\n\n`useOptimistic` updates the UI immediately while an async operation is in-flight. When the operation succeeds, the confirmed data replaces the optimistic value. If it fails, the UI reverts.\n\n### React 18 Pattern\n\n```jsx\n// React 18  manual optimistic update:\nfunction TodoList({ todos, onAddTodo }) {\n  const [optimistic, setOptimistic] = useState(todos);\n  \n  async function handleAddTodo(text) {\n    const newTodo = { id: Date.now(), text, completed: false };\n    \n    // Show optimistic update immediately\n    setOptimistic([...optimistic, newTodo]);\n    \n    try {\n      const result = await addTodo(text);\n      // Update with confirmed result\n      setOptimistic(prev =\u003e [\n        ...prev.filter(t =\u003e t.id !== newTodo.id),\n        result\n      ]);\n    } catch (err) {\n      // Revert on error\n      setOptimistic(optimistic);\n    }\n  }\n  \n  return (\n    \u003cul\u003e\n      {optimistic.map(todo =\u003e (\n        \u003cli key={todo.id}\u003e{todo.text}\u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n```\n\n### React 19 useOptimistic() Pattern\n\n```jsx\nimport { useOptimistic } from 'react';\n\nasync function addTodoAction(prevTodos, formData) {\n  const text = formData.get('text');\n  const result = await addTodo(text);\n  return [...prevTodos, result];\n}\n\nfunction TodoList({ todos }) {\n  const [optimistic, addOptimistic] = useOptimistic(\n    todos,\n    (state, newTodo) =\u003e [...state, newTodo]\n  );\n  \n  const [, formAction] = useActionState(addTodoAction, todos);\n  \n  async function handleAddTodo(formData) {\n    const text = formData.get('text');\n    // Optimistic update:\n    addOptimistic({ id: Date.now(), text, completed: false });\n    // Then call the form action:\n    formAction(formData);\n  }\n  \n  return (\n    \u003c\u003e\n      \u003cul\u003e\n        {optimistic.map(todo =\u003e (\n          \u003cli key={todo.id}\u003e{todo.text}\u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n      \u003cform action={handleAddTodo}\u003e\n        \u003cinput name=\"text\" /\u003e\n        \u003cbutton\u003eAdd\u003c/button\u003e\n      \u003c/form\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n**Key points:**\n\n- `useOptimistic(currentState, updateFunction)`\n- `updateFunction` receives `(state, optimisticInput)` and returns new state\n- Call `addOptimistic(input)` to trigger the optimistic update\n- The server action's return value replaces the optimistic state when done\n\n---\n\n## Full Example: Todo List with All Hooks\n\n```jsx\nimport { useActionState, useFormStatus, useOptimistic } from 'react';\n\n// Server action:\nasync function addTodoAction(prevTodos, formData) {\n  const text = formData.get('text');\n  if (!text) throw new Error('Text required');\n  const newTodo = await api.post('/todos', { text });\n  return [...prevTodos, newTodo];\n}\n\n// Submit button with useFormStatus:\nfunction AddButton() {\n  const { pending } = useFormStatus();\n  return \u003cbutton disabled={pending}\u003e{pending ? 'Adding...' : 'Add Todo'}\u003c/button\u003e;\n}\n\n// Main component:\nfunction TodoApp({ initialTodos }) {\n  const [optimistic, addOptimistic] = useOptimistic(\n    initialTodos,\n    (state, newTodo) =\u003e [...state, newTodo]\n  );\n  \n  const [todos, formAction] = useActionState(\n    addTodoAction,\n    initialTodos\n  );\n  \n  async function handleAddTodo(formData) {\n    const text = formData.get('text');\n    // Optimistic: show it immediately\n    addOptimistic({ id: Date.now(), text });\n    // Then submit the form (which updates when server confirms)\n    await formAction(formData);\n  }\n  \n  return (\n    \u003c\u003e\n      \u003cul\u003e\n        {optimistic.map(todo =\u003e (\n          \u003cli key={todo.id}\u003e{todo.text}\u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n      \u003cform action={handleAddTodo}\u003e\n        \u003cinput name=\"text\" placeholder=\"Add a todo...\" required /\u003e\n        \u003cAddButton /\u003e\n      \u003c/form\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n---\n\n## Migration Strategy\n\n### Phase 1  No changes required\n\nActions are opt-in. All existing `useReducer + onSubmit` patterns continue to work. No forced migration.\n\n### Phase 2  Identify refactor candidates\n\nAfter React 19 migration stabilizes, profile for `useReducer + async` patterns:\n\n```bash\ngrep -rn \"useReducer.*case.*'loading\\|useReducer.*case.*'success\" src/ --include=\"*.js\" --include=\"*.jsx\"\n```\n\nPatterns worth refactoring:\n\n- Form submissions with loading/error state\n- Async operations triggered by user events\n- Current code uses `dispatch({ type: '...' })`\n- Simple state shape (object with `loading`, `error`, `data`)\n\n### Phase 3  Refactor to useActionState\n\n```jsx\n// Before:\nfunction LoginForm() {\n  const [state, dispatch] = useReducer(loginReducer, { loading: false, error: null, user: null });\n  \n  async function handleSubmit(e) {\n    e.preventDefault();\n    dispatch({ type: 'loading' });\n    try {\n      const user = await login(e.target);\n      dispatch({ type: 'success', data: user });\n    } catch (err) {\n      dispatch({ type: 'error', error: err.message });\n    }\n  }\n  \n  return \u003cform onSubmit={handleSubmit}\u003e...\u003c/form\u003e;\n}\n\n// After:\nasync function loginAction(prevState, formData) {\n  try {\n    const user = await login(formData);\n    return { user, error: null };\n  } catch (err) {\n    return { user: null, error: err.message };\n  }\n}\n\nfunction LoginForm() {\n  const [state, formAction] = useActionState(loginAction, { user: null, error: null });\n  \n  return \u003cform action={formAction}\u003e...\u003c/form\u003e;\n}\n```\n\n---\n\n## Comparison Table\n\n| Feature | React 18 | React 19 |\n|---|---|---|\n| Form handling | `onSubmit` + useReducer | `action` + useActionState |\n| Loading state | Manual dispatch | Automatic `isPending` |\n| Child component pending state | Prop drilling | `useFormStatus` hook |\n| Optimistic updates | Manual state dance | `useOptimistic` hook |\n| Error handling | Manual in dispatch | Return from action |\n| Complexity | More boilerplate | Less boilerplate |\n","references/react19-suspense.md":"---\ntitle: React 19 Suspense for Data Fetching Pattern Reference\n---\n\n# React 19 Suspense for Data Fetching Pattern Reference\n\nReact 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`.\n\n**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.\n\n---\n\n## What Changed in React 19?\n\nReact 18 Suspense only supported **code splitting** (lazy components). React 19 extends it to **data fetching** if certain conditions are met:\n\n- **Lib usage**  the data fetching library must implement Suspense (e.g., React Query 5+, SWR, Remix loaders)\n- **Or your own promise tracking**  wrap promises in a way React can track their suspension\n- **No more \"no hook after suspense\"**  you can use Suspense directly in components with `use()`\n\n---\n\n## React 18 Suspense (Code Splitting Only)\n\n```jsx\n// React 18  Suspense for lazy imports only:\nconst LazyComponent = React.lazy(() =\u003e import('./Component'));\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cLazyComponent /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\nTrying to suspend for data in React 18 required hacks or libraries:\n\n```jsx\n// React 18 hack  not recommended:\nconst dataPromise = fetchData();\nconst resource = {\n  read: () =\u003e {\n    throw dataPromise; // Throw to suspend\n  }\n};\n\nfunction Component() {\n  const data = resource.read(); // Throws promise → Suspense catches it\n  return \u003cdiv\u003e{data}\u003c/div\u003e;\n}\n```\n\n---\n\n## React 19 Suspense for Data Fetching (Preview)\n\nReact 19 provides **first-class support** for Suspense with promises via the `use()` hook:\n\n```jsx\n// React 19  Suspense for data fetching:\nfunction UserProfile({ userId }) {\n  const user = use(fetchUser(userId)); // Suspends if promise pending\n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cUserProfile userId={123} /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n**Key differences from React 18:**\n\n- `use()` unwraps the promise, component suspends automatically\n- No need for `useEffect + state` trick\n- Cleaner code, less boilerplate\n\n---\n\n## Pattern 1: Simple Promise Suspense\n\n```jsx\n// Raw promise (not recommended in production):\nfunction DataComponent() {\n  const data = use(fetch('/api/data').then(r =\u003e r.json()));\n  return \u003cpre\u003e{JSON.stringify(data, null, 2)}\u003c/pre\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cDataComponent /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n**Problem:** Promise is recreated every render. Solution: wrap in `useMemo`.\n\n---\n\n## Pattern 2: Memoized Promise (Better)\n\n```jsx\nfunction DataComponent({ id }) {\n  // Only create promise once per id:\n  const dataPromise = useMemo(() =\u003e \n    fetch(`/api/data/${id}`).then(r =\u003e r.json()),\n    [id]\n  );\n  \n  const data = use(dataPromise);\n  return \u003cpre\u003e{JSON.stringify(data, null, 2)}\u003c/pre\u003e;\n}\n\nfunction App() {\n  const [id, setId] = useState(1);\n  \n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cDataComponent id={id} /\u003e\n      \u003cbutton onClick={() =\u003e setId(id + 1)}\u003eNext\u003c/button\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n---\n\n## Pattern 3: Library Integration (React Query)\n\nModern data libraries support Suspense directly. React Query 5+ example:\n\n```jsx\n// React Query 5+ with Suspense:\nimport { useSuspenseQuery } from '@tanstack/react-query';\n\nfunction UserProfile({ userId }) {\n  // useSuspenseQuery throws promise if suspended\n  const { data: user } = useSuspenseQuery({\n    queryKey: ['user', userId],\n    queryFn: () =\u003e fetchUser(userId),\n  });\n  \n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cUserProfile userId={123} /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n**Advantage:** Library handles caching, retries, and cache invalidation.\n\n---\n\n## Pattern 4: Error Boundary Integration\n\nCombine Suspense with Error Boundary to handle both loading and errors:\n\n```jsx\nfunction UserProfile({ userId }) {\n  const user = use(fetchUser(userId)); // Suspends while loading\n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cErrorBoundary fallback={\u003cErrorScreen /\u003e}\u003e\n      \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n        \u003cUserProfile userId={123} /\u003e\n      \u003c/Suspense\u003e\n    \u003c/ErrorBoundary\u003e\n  );\n}\n\nclass ErrorBoundary extends React.Component {\n  state = { error: null };\n  \n  static getDerivedStateFromError(error) {\n    return { error };\n  }\n  \n  render() {\n    if (this.state.error) return this.props.fallback;\n    return this.props.children;\n  }\n}\n```\n\n---\n\n## Nested Suspense Boundaries\n\nUse multiple Suspense boundaries to show partial UI while waiting for different data:\n\n```jsx\nfunction App({ userId }) {\n  return (\n    \u003cdiv\u003e\n      \u003cSuspense fallback={\u003cUserSpinner /\u003e}\u003e\n        \u003cUserProfile userId={userId} /\u003e\n      \u003c/Suspense\u003e\n      \n      \u003cSuspense fallback={\u003cPostsSpinner /\u003e}\u003e\n        \u003cUserPosts userId={userId} /\u003e\n      \u003c/Suspense\u003e\n    \u003c/div\u003e\n  );\n}\n\nfunction UserProfile({ userId }) {\n  const user = use(fetchUser(userId));\n  return \u003ch1\u003e{user.name}\u003c/h1\u003e;\n}\n\nfunction UserPosts({ userId }) {\n  const posts = use(fetchUserPosts(userId));\n  return \u003cul\u003e{posts.map(p =\u003e \u003cli key={p.id}\u003e{p.title}\u003c/li\u003e)}\u003c/ul\u003e;\n}\n```\n\nNow:\n\n- User profile shows spinner while loading\n- Posts show spinner independently\n- Both can render as they complete\n\n---\n\n## Sequential vs Parallel Suspense\n\n### Sequential (wait for first before fetching second)\n\n```jsx\nfunction App({ userId }) {\n  const user = use(fetchUser(userId)); // Must complete first\n  \n  return (\n    \u003cSuspense fallback={\u003cPostsSpinner /\u003e}\u003e\n      \u003cUserPosts userId={user.id} /\u003e {/* Depends on user */}\n    \u003c/Suspense\u003e\n  );\n}\n\nfunction UserPosts({ userId }) {\n  const posts = use(fetchUserPosts(userId));\n  return \u003cul\u003e{posts.map(p =\u003e \u003cli\u003e{p.title}\u003c/li\u003e)}\u003c/ul\u003e;\n}\n```\n\n### Parallel (fetch both at once)\n\n```jsx\nfunction App({ userId }) {\n  return (\n    \u003cdiv\u003e\n      \u003cSuspense fallback={\u003cUserSpinner /\u003e}\u003e\n        \u003cUserProfile userId={userId} /\u003e\n      \u003c/Suspense\u003e\n      \n      \u003cSuspense fallback={\u003cPostsSpinner /\u003e}\u003e\n        \u003cUserPosts userId={userId} /\u003e {/* Fetches in parallel */}\n      \u003c/Suspense\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n## Migration Strategy for React 18 → React 19\n\n### Phase 1  No changes required\n\nSuspense is still optional and experimental for data fetching. All existing `useEffect + state` patterns continue to work.\n\n### Phase 2  Wait for stability\n\nBefore adopting Suspense data fetching in production:\n\n- Wait for React 19 to ship (not preview)\n- Verify your data library supports Suspense\n- Plan migration after app stabilizes on React 19 core\n\n### Phase 3  Refactor to Suspense (optional, post-preview)\n\nOnce stable, profile candidates:\n\n```bash\ngrep -rn \"useEffect.*fetch\\|useEffect.*axios\\|useEffect.*graphql\" src/ --include=\"*.js\" --include=\"*.jsx\"\n```\n\n```jsx\n// Before (React 18):\nfunction UserProfile({ userId }) {\n  const [user, setUser] = useState(null);\n  \n  useEffect(() =\u003e {\n    fetchUser(userId).then(setUser);\n  }, [userId]);\n  \n  if (!user) return \u003cSpinner /\u003e;\n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n\n// After (React 19 with Suspense):\nfunction UserProfile({ userId }) {\n  const user = use(fetchUser(userId));\n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n\n// Must be wrapped in Suspense:\n\u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n  \u003cUserProfile userId={123} /\u003e\n\u003c/Suspense\u003e\n```\n\n---\n\n## Important Warnings\n\n1. **Still Preview**  Suspense for data is marked experimental, behavior may change\n2. **Performance**  promises are recreated on every render without memoization; use `useMemo`\n3. **Cache**  `use()` doesn't cache; use React Query or similar for production apps\n4. **SSR**  Suspense SSR support is limited; check Next.js version requirements\n","references/react19-use.md":"---\ntitle: React 19 use() Hook Pattern Reference\n---\n\n# React 19 use() Hook Pattern Reference\n\nThe `use()` hook is React 19's answer for unwrapping promises and context within React components. It enables cleaner async patterns directly in your component body, avoiding the architectural complexity that previously required separate lazy components or complex state management.\n\n## What is use()?\n\n`use()` is a hook that:\n\n- **Accepts** a promise or context object\n- **Returns** the resolved value or context value\n- **Handles** Suspense automatically for promises\n- **Can be called conditionally** inside components (not at top level for promises)\n- **Throws errors**, which Suspense + error boundary can catch\n\n## use() with Promises\n\n### React 18 Pattern\n\n```jsx\n// React 18 approach 1  lazy load a component module:\nconst UserComponent = React.lazy(() =\u003e import('./User'));\n\nfunction App() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cUserComponent /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n\n// React 18 approach 2  fetch data with state + useEffect:\nfunction App({ userId }) {\n  const [user, setUser] = useState(null);\n  \n  useEffect(() =\u003e {\n    fetchUser(userId).then(setUser);\n  }, [userId]);\n  \n  if (!user) return \u003cSpinner /\u003e;\n  return \u003cUser user={user} /\u003e;\n}\n```\n\n### React 19 use() Pattern\n\n```jsx\n// React 19  use() directly in component:\nfunction App({ userId }) {\n  const user = use(fetchUser(userId)); // Suspends automatically\n  return \u003cUser user={user} /\u003e;\n}\n\n// Usage:\nfunction Root() {\n  return (\n    \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n      \u003cApp userId={123} /\u003e\n    \u003c/Suspense\u003e\n  );\n}\n```\n\n**Key differences:**\n\n- `use()` unwraps the promise directly in the component body\n- Suspense boundary is still needed, but can be placed at app root (not per-component)\n- No state or useEffect needed for simple async data\n- Conditional wrapping allowed inside components\n\n## use() with Promises -- Conditional Fetching\n\n```jsx\n// React 18  conditional with state\nfunction SearchResults() {\n  const [results, setResults] = useState(null);\n  const [query, setQuery] = useState('');\n  \n  useEffect(() =\u003e {\n    if (query) {\n      search(query).then(setResults);\n    } else {\n      setResults(null);\n    }\n  }, [query]);\n  \n  if (!results) return null;\n  return \u003cResults items={results} /\u003e;\n}\n\n// React 19  use() with conditional\nfunction SearchResults() {\n  const [query, setQuery] = useState('');\n  \n  if (!query) return null;\n  \n  const results = use(search(query)); // Only fetches if query is truthy\n  return \u003cResults items={results} /\u003e;\n}\n```\n\n## use() with Context\n\n`use()` can unwrap context without being at component root. Less common than promise usage, but useful for conditional context reading:\n\n```jsx\n// React 18  always in component body, only works at top level\nconst theme = useContext(ThemeContext);\n\n// React 19  can be conditional\nfunction Button({ useSystemTheme }) {\n  const theme = useSystemTheme ? use(ThemeContext) : defaultTheme;\n  return \u003cbutton style={theme}\u003eClick\u003c/button\u003e;\n}\n```\n\n---\n\n## Migration Strategy\n\n### Phase 1  No changes required\n\nReact 19 `use()` is opt-in. All existing Suspense + component splitting patterns continue to work:\n\n```jsx\n// Keep this as-is if it's working:\nconst Lazy = React.lazy(() =\u003e import('./Component'));\n\u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\u003cLazy /\u003e\u003c/Suspense\u003e\n```\n\n### Phase 2  Post-migration cleanup (optional)\n\nAfter React 19 migration stabilizes, profile codebases for `useEffect + state` async patterns. These are good candidates for `use()` refactoring:\n\nIdentify patterns:\n\n```bash\ngrep -rn \"useEffect.*\\(.*fetch\\|async\\|promise\" src/ --include=\"*.js\" --include=\"*.jsx\"\n```\n\nTarget:\n\n- Simple fetch-on-mount patterns\n- No complex dependency arrays\n- Single promise per component\n- Suspense already in use elsewhere in the app\n\nExample refactor:\n\n```jsx\n// Before:\nfunction Post({ postId }) {\n  const [post, setPost] = useState(null);\n  \n  useEffect(() =\u003e {\n    fetchPost(postId).then(setPost);\n  }, [postId]);\n  \n  if (!post) return \u003cSpinner /\u003e;\n  return \u003cPostContent post={post} /\u003e;\n}\n\n​// After:\nfunction Post({ postId }) {\n  const post = use(fetchPost(postId));\n  return \u003cPostContent post={post} /\u003e;\n}\n\n// And ensure Suspense at app level:\n\u003cSuspense fallback={\u003cAppSpinner /\u003e}\u003e\n  \u003cPost postId={123} /\u003e\n\u003c/Suspense\u003e\n```\n\n---\n\n## Error Handling\n\n`use()` throws errors, which Suspense error boundaries catch:\n\n```jsx\nfunction Root() {\n  return (\n    \u003cErrorBoundary fallback={\u003cErrorScreen /\u003e}\u003e\n      \u003cSuspense fallback={\u003cSpinner /\u003e}\u003e\n        \u003cDataComponent /\u003e\n      \u003c/Suspense\u003e\n    \u003c/ErrorBoundary\u003e\n  );\n}\n\nfunction DataComponent() {\n  const data = use(fetchData()); // If fetch rejects, error boundary catches it\n  return \u003cData data={data} /\u003e;\n}\n```\n\n---\n\n## When NOT to use use()\n\n- **Avoid during migration**  stabilize React 19 first\n- **Complex dependencies**  if multiple promises or complex ordering logic, stick with `useEffect`\n- **Retry logic**  `use()` doesn't handle retry; `useEffect` with state is clearer\n- **Debounced updates**  `use()` refetches on every prop change; `useEffect` with cleanup is better\n"},"import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/tree/541b7819d8c3545c6df122491af4fa1eae415779/plugins/react19-upgrade/skills/react19-concurrent-patterns"}},"content_hash":[168,174,157,57,175,83,132,223,224,123,242,52,203,209,244,47,100,4,29,17,192,199,8,165,160,249,117,194,153,68,67,212],"trust_level":"unsigned","yanked":false}
