{"record":{"id":"82ebab2065170046","repo":"facebook/react","slug":"hooks-are-not-supported-inside-an-async-component","errorCode":null,"errorMessage":"Hooks are not supported inside an async component. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.","messagePattern":"Hooks are not supported inside an async component\\. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-reconciler/src/ReactFiberThenable.js","lineNumber":422,"sourceCode":"      return true;\n    }\n  }\n  return false;\n}\n\nexport function checkIfUseWrappedInAsyncCatch(rejectedReason: any) {\n  // This check runs in prod, too, because it prevents a more confusing\n  // downstream error, where SuspenseException is caught by a promise and\n  // thrown asynchronously.\n  // TODO: Another way to prevent SuspenseException from leaking into an async\n  // execution context is to check the dispatcher every time `use` is called,\n  // or some equivalent. That might be preferable for other reasons, too, since\n  // it matches how we prevent similar mistakes for other hooks.\n  if (\n    rejectedReason === SuspenseException ||\n    rejectedReason === SuspenseActionException\n  ) {\n    throw new Error(\n      'Hooks are not supported inside an async component. This ' +\n        \"error is often caused by accidentally adding `'use client'` \" +\n        'to a module that was originally written for the server.',\n    );\n  }\n}\n\nfunction areSameKeyPath(a: Fiber, b: Fiber): boolean {\n  if (a === b) {\n    return true;\n  }\n  if (\n    a.tag !== b.tag ||\n    a.type !== b.type ||\n    a.key !== b.key ||\n    a.index !== b.index\n  ) {\n    return false;","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-reconciler/src/ReactFiberThenable.js#L404-L440","documentation":"React async components are only supported on the server. When a module marked 'use client' exports an async component, hooks that suspend (use() and Suspense-backed hooks) throw React's internal SuspenseException; inside an async function that exception escapes into the promise rejection machinery instead of reaching React. checkIfUseWrappedInAsyncCatch inspects every rejection caught inside an async component and, if it is SuspenseException or SuspenseActionException, replaces it with this readable error. The check runs in production builds too, because the leaked exception would otherwise resurface later as a far more confusing asynchronous error.","triggerScenarios":"Exporting an async function component from a file with 'use client' at the top, then letting a hook suspend inside it: the thrown SuspenseException is captured by the async function's own try/catch or an await rejection path, routed through checkIfUseWrappedInAsyncCatch, and converted into this throw.","commonSituations":"Adding 'use client' to a file that was originally a Server Component so it can use state while keeping async/await data fetching; copying RSC patterns (async function Page() { const d = await ... }) into a client bundle; Next.js App Router conversions where a page needs browser APIs but keeps await-style fetching.","solutions":["Remove 'use client' from the module and keep the async component as a Server Component that passes data down to non-async client children","If the component must be interactive, make it synchronous and read data with the use() hook wrapped in <Suspense>, or fetch in effects","Split the file: keep the await-based data fetch in a server component and move interactive logic to a separate non-async client component"],"exampleFix":"// before\n'use client';\nexport default async function Profile() {\n  const data = await fetchUser(); // suspends; SuspenseException leaks into async context\n  return <Details data={data} />;\n}\n\n// after\n// Profile.js (server, no 'use client')\nimport Details from './Details';\nexport default async function Profile() {\n  const data = await fetchUser();\n  return <Details data={data} />;\n}\n// Details.js ('use client', synchronous)\n'use client';\nexport default function Details({data}) {\n  return <pre>{JSON.stringify(data)}</pre>;\n}","handlingStrategy":"validation","validationCode":"// CI check: flag modules that are client-side AND export async components\nconst isAsyncClientModule = (source) =>\n  /['\"]use client['\"]/.test(source) &&\n  /export\\s+(default\\s+)?async\\s+function/.test(source);\nif (isAsyncClientModule(source)) {\n  fail(`${file}: async client components cannot use hooks — move awaits to a server component`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never export async function components from modules marked 'use client'","Keep await-based data fetching in Server Components and pass results as props to synchronous client components","For suspense data in client components, use the use() hook inside a synchronous component wrapped in <Suspense>"],"tags":["hooks","async-components","use-client","suspense","server-components"],"backgroundTag":"hooks-in-async-component","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}