Skip to content

Replacing Legacy throw in React 19 with Suspense and use

Updated: at 12:10 AM

I’ve been closely watching the evolution of React 19, and in particlar how it has changed to support Suspense in client only apps. When React 18 released, the behavior associated with Suspense was to use throw as a helper to figure out when promises completed, and there by elements enclosed in a Suspense boundary could be rendered.

I recently noticed this comment:

This feature is implemented for both use and for the legacy promise throwing behavior.

in this issue. I had not realized that the throw behavior was now legacy. So, I asked ChatGPT about it and this is what it said. It makes total sense to me so I thought I’d blog it in case anyone else is interested.

First, a bit of context: React uses Suspense to “pause” rendering when some data isn’t ready yet—traditionally, this has worked by literally throwing a Promise inside a component. React catches that thrown Promise and uses it as a trigger to show a fallback until the Promise resolves (or rejects). That’s often referred to as the “legacy promise throwing behavior.”

From ChatGPT o1:

Although it’s a bit unorthodox to literally throw a Promise, this approach became an (undocumented) standard pattern for server- and client-side Suspense.

New .use() Behavior

According to the linked GitHub issue, React’s .use() will eventually be the recommended way to handle async resources. However, for backward-compatibility, the “legacy promise throwing behavior” is still supported. In other words, even if you don’t switch over to .use(), the old mechanism of throwing a Promise is not going away immediately; React continues to handle both so users aren’t forced to migrate in one big step.

Summary

  1. Legacy Behavior: Throw a Promise from your component to trigger Suspense.
  2. Current (New) Behavior: Call React.use(resource) to trigger Suspense without manually throwing.
  3. Both Supported: React continues to honor the old promise-throwing approach for backward compatibility, while encouraging the new .use() API for clarity and future-proofing.
Check out the ORM (Object Relational Mapper) PRISMA. The database access method I use in all my projects