{"record":{"id":"a84d9f1156431410","repo":"facebook/react","slug":"426","errorCode":"426","errorMessage":"A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.","messagePattern":"A component suspended while responding to synchronous input\\. This will cause the UI to be replaced with a loading indicator\\. To fix, updates that suspend should be wrapped with startTransition\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-dom/src/server/ReactDOMLegacyServerImpl.js","lineNumber":94,"sourceCode":"    onShellReady,\n    undefined,\n    undefined,\n    undefined,\n  );\n  startWork(request);\n  // If anything suspended and is still pending, we'll abort it before writing.\n  // That way we write only client-rendered boundaries from the start.\n  abort(request, abortReason);\n  startFlowing(request, destination);\n  if (didFatal && fatalError !== abortReason) {\n    throw fatalError;\n  }\n\n  if (!readyToStream) {\n    // Note: This error message is the one we use on the client. It doesn't\n    // really make sense here. But this is the legacy server renderer, anyway.\n    // We're going to delete it soon.\n    throw new Error(\n      'A component suspended while responding to synchronous input. This ' +\n        'will cause the UI to be replaced with a loading indicator. To fix, ' +\n        'updates that suspend should be wrapped with startTransition.',\n    );\n  }\n\n  return result;\n}\n\nexport {renderToStringImpl, ReactVersion as version};\n","sourceCodeStart":76,"sourceCodeEnd":105,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-dom/src/server/ReactDOMLegacyServerImpl.js#L76-L105","documentation":"renderToStringImpl powers the legacy synchronous renderers (renderToString/renderToStaticMarkup). It starts a Fizz request, aborts still-pending Suspense boundaries, and then checks the onShellReady flag; if the shell never became ready — something suspended and blocked synchronous completion — it throws this message (code 426). The source comments themselves note the client-worded message does not really fit here and the legacy renderer is slated for deletion.","triggerScenarios":"Calling renderToString() or renderToStaticMarkup() on a tree whose shell suspends and cannot finish synchronously: a lazy() component, awaited promise (use()), or data fetch above any <Suspense> boundary, so onShellReady never fires before the abort.","commonSituations":"Legacy SSR pipelines feeding renderToString output into page templates; email/HTML generation with components that newly added suspenseful data loading; upgrading trees whose top level now suspends.","solutions":["Move to renderToPipeableStream (Node) or renderToReadableStream (edge/browser), which support Suspense and streaming","Wrap suspending components in <Suspense fallback={...}> so the shell completes without them","Preload or await the data before calling renderToString so nothing suspends","Keep lazy()/async resources out of the shell level of the tree"],"exampleFix":"// before\nconst html = renderToString(<App />); // App suspends in the shell\n\n// after\n// 1) add a Suspense boundary around the suspending part\n<Suspense fallback={<Loading />}><SlowContent /></Suspense>\n// 2) or use the streaming API\nconst {pipe} = renderToPipeableStream(<App />, {onShellReady() { pipe(res); }});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let html;\ntry {\n  html = renderToString(<App />);\n} catch (e) {\n  if (\n    e instanceof Error &&\n    e.message.includes('A component suspended while responding to synchronous input')\n  ) {\n    throw new Error(\n      'renderToString blocked on Suspense — wrap suspending content in <Suspense> or switch to renderToPipeableStream'\n    );\n  }\n  throw e;\n}","preventionTips":["Keep the top of the tree free of lazy()/use()/awaited data when using renderToString","Wrap any suspending subtree in a <Suspense> boundary with a fallback","Prefer streaming renderers (renderToPipeableStream/renderToReadableStream) for new SSR code","Add SSR smoke tests so new suspenseful data loading cannot silently break string rendering"],"tags":["react-dom","ssr","suspense","rendertostring","legacy-api"],"backgroundTag":"suspense-blocking-render-to-string","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}