Repository: react
Issue: Suspense API forces coupling of page-load sequence with runtime updates
Labels: Type: Discussion,Component: Suspense,Component: Concurrent Features

_(the prelude to this issue may be a bit abstract, but please bear with me!)_

## Problem statement
As it has usually been the case in React and already stated in [its docs](https://reactjs.org/blog/2022/03/29/react-v18.html#what-is-concurrent-react):

> When we [React] design APIs, we try to hide implementation details from developers. As a React developer, you focus on what you want the user experience to look like, and React handles how to deliver that experience...

But I think the new Suspense API -with all of its great features- contradicts this mindset to some degree.
Because looking at the library from the “usage” perspective, Suspense API enforces coupling of independent features together. Features that although are related from a technical standpoint, in reality can be fundamentally different.
- For example, code-splitting is basically a separate subject from the other features. Because a developer may need code-splitting (for bundle size optimizations), but it doesn’t necessarily mean they want to render the lazy-loaded component in a delayed manner in the initial html coming from the SSR stream. They also may not want to delay that component’s hydration after other (non-suspense) parts are hydrated either.

But the main issue I’m referring to is about the other Suspense features that basically seem to enable a “federated-mode” (for the lack of a better word) that makes each suspended component act independently in the tree.
The problem is, they are coupling “SSR and page-load sequence” with “client-side runtime” and forcing product decisions on developers. Because:
- On SSR, a developer may want to use partial streaming and selective/async hydration to provide performance boost for the initial page-load sequence. For example, if an end user initially opens a page that includes the details of an article scattered among multiple nested components, it makes sense to stream/render/hydrate different components of the article’s details (photos, title/description, ratings, comments, etc.) separately and as soon as possible (with some optional priorities).
- But the developer doesn't necessarily need the application to act in the same way for the future updates and renders on the client side when a **change** happens. So for example when the user clicks to show a similar article and the page has to update on the client side to show the data of this different article, the developer should have the choice to make the page’s content (photos, title/description, ratings, comments, etc.) update together in a single graceful step - or batch some of the nested components’ loadings together (regardless of whether nested suspense boundaries were used). As opposed to each component getting updated independently, showing their own fallbacks independently, and rendering the new content independently while another component is still showing the old content or a fallback. Because this can cause discrepancies on the page, and aside from that, is inherently forcing a product decision on the developer. Basically, how/where to show fallback/loading states is a product/UX decision and the API shouldn’t force an approach.
- Last but not least, regardless of all the previous usages of Suspense, it should still be possible to opt into **interruptible/concurrent rendering** on client side during runtime changes (after page-load) which allows pausing a rendering sequence and prioritizing user interactions, to benefit from the improvements to the user experience and the perceived performance.

## Working example
I think [this issue](https://github.com/facebook/react/issues/25082) is a great practical use-case of the problem of coupling behaviors on "SSR/page-load sequence" and "client-side changes" **even with the use of the `useTransition` api**, which also includes a working codeSandbox example.

## Suggested solution
I’m not sure what the "best" solution would be (e.g. to have a separate component other than Suspense, or change the `useTransition` behavior as suggested in the [previously mentioned issue](https://github.com/facebook/react/issues/25082) or…), but a solution that can also work is:

Adding two new props to the `Suspense` component like:
- `hoistSuspenseHandling`: If `true`, skips the handling of the Suspense status (to show a fallback etc) inside component’s own suspense boundary, and instead give it to its parent to be merged with parent’s Suspense handling (if accepted by the parent).
- `handleChildrenSuspense`: The other side of the previous prop, acting as a safety net to ensure the parent can (wants to) handle its children’s suspense. It is passed to the parent Suspense component. If `true`, it will allow merging of the suspense conditions of children with its own suspense handling.

(Depending on the real life usage, they could instead be functions passed to the Suspended component as well, so it can change the values internally during runtime)
Also a component that has both `hoistSuspenseHandling` and `handleChildrenSuspense` as `true`, could add its children’s suspense conditions to its own and pass them all up to the parent.

With this, a developer can differentiate between SSR/page-load sequence and client side updates/changes (or even more different scenarios) and handle components’ fallback/loading states in different places on the tree.