-
Notifications
You must be signed in to change notification settings - Fork 50k
[Fizz] Enable the progressiveChunkSize option #33027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
We still need to complete the fallback in case we outline the boundary.
| pendingRootTasks: number, // when this reaches zero, we've finished at least the root boundary. | ||
| completedRootSegment: null | Segment, // Completed but not yet flushed root segments. | ||
| completedPreambleSegments: null | Array<Array<Segment>>, // contains the ready-to-flush segments that make up the preamble | ||
| byteSize: number, // counts the number of bytes accumulated in the shell |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems this isn't doing anything for now. But your comment didn't indicate that you have plans for tracking the size of the root. I suppose if the root was tiny we ought to still include even large boundaries because there isn't anything else being blocked that is meaningful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using it in #33029
Hardanish-Singh
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Since the very beginning we have had the `progressiveChunkSize` option but we never actually took advantage of it because we didn't count the bytes that we emitted. This starts counting the bytes by taking a pass over the added chunks each time a segment completes. That allows us to outline a Suspense boundary to stream in late even if it is already loaded by the time that back-pressure flow and in a `prerender`. Meaning it gets inserted with script. The effect can be seen in the fixture where if you have large HTML content that can block initial paint (thanks to [`rel="expect"`](#33016) but also nested Suspense boundaries). Before this fix, the paint would be blocked until the large content loaded. This lets us paint the fallback first in the case that the raw bytes of the content takes a while to download. You can set it to `Infinity` to opt-out. E.g. if you want to ensure there's never any scripts. It's always set to `Infinity` in `renderToHTML` and the legacy `renderToString`. One downside is that if we might choose to outline a boundary, we need to let its fallback complete. We don't currently discount the size of the fallback but really just consider them additive even though in theory the fallback itself could also add significant size or even more than the content. It should maybe really be considered the delta but that would require us to track the size of the fallback separately which is tricky. One problem with the current heuristic is that we just consider the size of the boundary content itself down to the next boundary. If you have a lot of small boundaries adding up, it'll never kick in. I intend to address that in a follow up. DiffTrain build for [8e9a5fc](8e9a5fc)
Since the very beginning we have had the `progressiveChunkSize` option but we never actually took advantage of it because we didn't count the bytes that we emitted. This starts counting the bytes by taking a pass over the added chunks each time a segment completes. That allows us to outline a Suspense boundary to stream in late even if it is already loaded by the time that back-pressure flow and in a `prerender`. Meaning it gets inserted with script. The effect can be seen in the fixture where if you have large HTML content that can block initial paint (thanks to [`rel="expect"`](facebook#33016) but also nested Suspense boundaries). Before this fix, the paint would be blocked until the large content loaded. This lets us paint the fallback first in the case that the raw bytes of the content takes a while to download. You can set it to `Infinity` to opt-out. E.g. if you want to ensure there's never any scripts. It's always set to `Infinity` in `renderToHTML` and the legacy `renderToString`. One downside is that if we might choose to outline a boundary, we need to let its fallback complete. We don't currently discount the size of the fallback but really just consider them additive even though in theory the fallback itself could also add significant size or even more than the content. It should maybe really be considered the delta but that would require us to track the size of the fallback separately which is tricky. One problem with the current heuristic is that we just consider the size of the boundary content itself down to the next boundary. If you have a lot of small boundaries adding up, it'll never kick in. I intend to address that in a follow up. DiffTrain build for [8e9a5fc](facebook@8e9a5fc)
…pletion (#33029) Follow up to #33027. This enhances the heuristic so that we accumulate the size of the currently written boundaries. Starting from the size of the root (minus preamble) for the shell. This ensures that if you have many small boundaries they don't all continue to get inlined. For example, you can wrap each paragraph in a document in a Suspense boundary to regain document streaming capabilities if that's what you want. However, one consideration is if it's worth producing a fallback at all. Maybe if it's like `null` it's free but if it's like a whole alternative page, then it's not. It's possible to have completely useless Suspense boundaries such as when you nest several directly inside each other. So this uses a limit of at least 500 bytes of the content itself for it to be worth outlining at all. It also can't be too small because then for example a long list of paragraphs can never be outlined. In the fixture I straddle this limit so some paragraphs are too small to be considered. An unfortunate effect of that is that you can end up with some of them not being outlined which means that they appear out of order. SuspenseList is supposed to address that but it's unfortunate. The limit is still fairly high though so it's unlikely that by default you'd start outlining anything within the viewport at all. I had to reduce the `progressiveChunkSize` by an order of magnitude in my fixture to try it out properly.
…pletion (#33029) Follow up to #33027. This enhances the heuristic so that we accumulate the size of the currently written boundaries. Starting from the size of the root (minus preamble) for the shell. This ensures that if you have many small boundaries they don't all continue to get inlined. For example, you can wrap each paragraph in a document in a Suspense boundary to regain document streaming capabilities if that's what you want. However, one consideration is if it's worth producing a fallback at all. Maybe if it's like `null` it's free but if it's like a whole alternative page, then it's not. It's possible to have completely useless Suspense boundaries such as when you nest several directly inside each other. So this uses a limit of at least 500 bytes of the content itself for it to be worth outlining at all. It also can't be too small because then for example a long list of paragraphs can never be outlined. In the fixture I straddle this limit so some paragraphs are too small to be considered. An unfortunate effect of that is that you can end up with some of them not being outlined which means that they appear out of order. SuspenseList is supposed to address that but it's unfortunate. The limit is still fairly high though so it's unlikely that by default you'd start outlining anything within the viewport at all. I had to reduce the `progressiveChunkSize` by an order of magnitude in my fixture to try it out properly. DiffTrain build for [18212ca](18212ca)
…pletion (facebook#33029) Follow up to facebook#33027. This enhances the heuristic so that we accumulate the size of the currently written boundaries. Starting from the size of the root (minus preamble) for the shell. This ensures that if you have many small boundaries they don't all continue to get inlined. For example, you can wrap each paragraph in a document in a Suspense boundary to regain document streaming capabilities if that's what you want. However, one consideration is if it's worth producing a fallback at all. Maybe if it's like `null` it's free but if it's like a whole alternative page, then it's not. It's possible to have completely useless Suspense boundaries such as when you nest several directly inside each other. So this uses a limit of at least 500 bytes of the content itself for it to be worth outlining at all. It also can't be too small because then for example a long list of paragraphs can never be outlined. In the fixture I straddle this limit so some paragraphs are too small to be considered. An unfortunate effect of that is that you can end up with some of them not being outlined which means that they appear out of order. SuspenseList is supposed to address that but it's unfortunate. The limit is still fairly high though so it's unlikely that by default you'd start outlining anything within the viewport at all. I had to reduce the `progressiveChunkSize` by an order of magnitude in my fixture to try it out properly. DiffTrain build for [18212ca](facebook@18212ca)
…pletion (#33029) Follow up to #33027. This enhances the heuristic so that we accumulate the size of the currently written boundaries. Starting from the size of the root (minus preamble) for the shell. This ensures that if you have many small boundaries they don't all continue to get inlined. For example, you can wrap each paragraph in a document in a Suspense boundary to regain document streaming capabilities if that's what you want. However, one consideration is if it's worth producing a fallback at all. Maybe if it's like `null` it's free but if it's like a whole alternative page, then it's not. It's possible to have completely useless Suspense boundaries such as when you nest several directly inside each other. So this uses a limit of at least 500 bytes of the content itself for it to be worth outlining at all. It also can't be too small because then for example a long list of paragraphs can never be outlined. In the fixture I straddle this limit so some paragraphs are too small to be considered. An unfortunate effect of that is that you can end up with some of them not being outlined which means that they appear out of order. SuspenseList is supposed to address that but it's unfortunate. The limit is still fairly high though so it's unlikely that by default you'd start outlining anything within the viewport at all. I had to reduce the `progressiveChunkSize` by an order of magnitude in my fixture to try it out properly. DiffTrain build for [18212ca960ee2f0acf538c2198f7ba36c3042ecd](https://github.com/facebook/react/commit/18212ca960ee2f0acf538c2198f7ba36c3042ecd)
|
If a chunk exceeds The docco for
- https://react.dev/reference/react-dom/server/renderToReadableStream#returns |
This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react)) | [`19.1.10` -> `19.2.6`](https://renovatebot.com/diffs/npm/@types%2freact/19.1.10/19.2.6) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom)) | [`19.1.7` -> `19.2.3`](https://renovatebot.com/diffs/npm/@types%2freact-dom/19.1.7/19.2.3) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [react](https://react.dev/) ([source](https://github.com/facebook/react/tree/HEAD/packages/react)) | [`19.1.1` -> `19.2.0`](https://renovatebot.com/diffs/npm/react/19.1.1/19.2.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [react-dom](https://react.dev/) ([source](https://github.com/facebook/react/tree/HEAD/packages/react-dom)) | [`19.1.1` -> `19.2.0`](https://renovatebot.com/diffs/npm/react-dom/19.1.1/19.2.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>facebook/react (react)</summary> ### [`v19.2.0`](https://github.com/facebook/react/blob/HEAD/CHANGELOG.md#1920-October-1st-2025) [Compare Source](facebook/react@v19.1.1...v19.2.0) Below is a list of all new features, APIs, and bug fixes. Read the [React 19.2 release post](https://react.dev/blog/2025/10/01/react-19-2) for more information. ##### New React Features - [`<Activity>`](https://react.dev/reference/react/Activity): A new API to hide and restore the UI and internal state of its children. - [`useEffectEvent`](https://react.dev/reference/react/useEffectEvent) is a React Hook that lets you extract non-reactive logic into an [Effect Event](https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event). - [`cacheSignal`](https://react.dev/reference/react/cacheSignal) (for RSCs) lets your know when the `cache()` lifetime is over. - [React Performance tracks](https://react.dev/reference/dev-tools/react-performance-tracks) appear on the Performance panel’s timeline in your browser developer tools ##### New React DOM Features - Added resume APIs for partial pre-rendering with Web Streams: - [`resume`](https://react.dev/reference/react-dom/server/resume): to resume a prerender to a stream. - [`resumeAndPrerender`](https://react.dev/reference/react-dom/static/resumeAndPrerender): to resume a prerender to HTML. - Added resume APIs for partial pre-rendering with Node Streams: - [`resumeToPipeableStream`](https://react.dev/reference/react-dom/server/resumeToPipeableStream): to resume a prerender to a stream. - [`resumeAndPrerenderToNodeStream`](https://react.dev/reference/react-dom/static/resumeAndPrerenderToNodeStream): to resume a prerender to HTML. - Updated [`prerender`](https://react.dev/reference/react-dom/static/prerender) APIs to return a `postponed` state that can be passed to the `resume` APIs. ##### Notable changes - React DOM now batches suspense boundary reveals, matching the behavior of client side rendering. This change is especially noticeable when animating the reveal of Suspense boundaries e.g. with the upcoming `<ViewTransition>` Component. React will batch as much reveals as possible before the first paint while trying to hit popular first-contentful paint metrics. - Add Node Web Streams (`prerender`, `renderToReadableStream`) to server-side-rendering APIs for Node.js - Use underscore instead of `:` IDs generated by useId ##### All Changes ##### React - `<Activity />` was developed over many years, starting before `ClassComponent.setState` ([@​acdlite](https://github.com/acdlite) [@​sebmarkbage](https://github.com/sebmarkbage) and many others) - Stringify context as "SomeContext" instead of "SomeContext.Provider" ([@​kassens](https://github.com/kassens) [#​33507](facebook/react#33507)) - Include stack of cause of React instrumentation errors with `%o` placeholder ([@​eps1lon](https://github.com/eps1lon) [#​34198](facebook/react#34198)) - Fix infinite `useDeferredValue` loop in popstate event ([@​acdlite](https://github.com/acdlite) [#​32821](facebook/react#32821)) - Fix a bug when an initial value was passed to `useDeferredValue` ([@​acdlite](https://github.com/acdlite) [#​34376](facebook/react#34376)) - Fix a crash when submitting forms with Client Actions ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33055](facebook/react#33055)) - Hide/unhide the content of dehydrated suspense boundaries if they resuspend ([@​sebmarkbage](https://github.com/sebmarkbage) [#​32900](facebook/react#32900)) - Avoid stack overflow on wide trees during Hot Reload ([@​sophiebits](https://github.com/sophiebits) [#​34145](facebook/react#34145)) - Improve Owner and Component stacks in various places ([@​sebmarkbage](https://github.com/sebmarkbage), [@​eps1lon](https://github.com/eps1lon): [#​33629](facebook/react#33629), [#​33724](facebook/react#33724), [#​32735](facebook/react#32735), [#​33723](facebook/react#33723)) - Add `cacheSignal` ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33557](facebook/react#33557)) ##### React DOM - Block on Suspensey Fonts during reveal of server-side-rendered content ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33342](facebook/react#33342)) - Use underscore instead of `:` for IDs generated by `useId` ([@​sebmarkbage](https://github.com/sebmarkbage), [@​eps1lon](https://github.com/eps1lon): [#​32001](facebook/react#32001), [#​33342](https://github.com/facebook/react/pull/33342)[#​33099](https://github.com/facebook/react/pull/33099), [#​33422](facebook/react#33422)) - Stop warning when ARIA 1.3 attributes are used ([@​Abdul-Omira](https://github.com/Abdul-Omira) [#​34264](facebook/react#34264)) - Allow `nonce` to be used on hoistable styles ([@​Andarist](https://github.com/Andarist) [#​32461](facebook/react#32461)) - Warn for using a React owned node as a Container if it also has text content ([@​sebmarkbage](https://github.com/sebmarkbage) [#​32774](facebook/react#32774)) - s/HTML/text for for error messages if text hydration mismatches ([@​rickhanlonii](https://github.com/rickhanlonii) [#​32763](facebook/react#32763)) - Fix a bug with `React.use` inside `React.lazy`-ed Component ([@​hi-ogawa](https://github.com/hi-ogawa) [#​33941](facebook/react#33941)) - Enable the `progressiveChunkSize` option for server-side-rendering APIs ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33027](facebook/react#33027)) - Fix a bug with deeply nested Suspense inside Suspense fallback when server-side-rendering ([@​gnoff](https://github.com/gnoff) [#​33467](facebook/react#33467)) - Avoid hanging when suspending after aborting while rendering ([@​gnoff](https://github.com/gnoff) [#​34192](facebook/react#34192)) - Add Node Web Streams to server-side-rendering APIs for Node.js ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33475](facebook/react#33475)) ##### React Server Components - Preload `<img>` and `<link>` using hints before they're rendered ([@​sebmarkbage](https://github.com/sebmarkbage) [#​34604](facebook/react#34604)) - Log error if production elements are rendered during development ([@​eps1lon](https://github.com/eps1lon) [#​34189](facebook/react#34189)) - Fix a bug when returning a Temporary reference (e.g. a Client Reference) from Server Functions ([@​sebmarkbage](https://github.com/sebmarkbage) [#​34084](facebook/react#34084), [@​denk0403](https://github.com/denk0403) [#​33761](facebook/react#33761)) - Pass line/column to `filterStackFrame` ([@​eps1lon](https://github.com/eps1lon) [#​33707](facebook/react#33707)) - Support Async Modules in Turbopack Server References ([@​lubieowoce](https://github.com/lubieowoce) [#​34531](facebook/react#34531)) - Add support for .mjs file extension in Webpack ([@​jennyscript](https://github.com/jennyscript) [#​33028](facebook/react#33028)) - Fix a wrong missing key warning ([@​unstubbable](https://github.com/unstubbable) [#​34350](facebook/react#34350)) - Make console log resolve in predictable order ([@​sebmarkbage](https://github.com/sebmarkbage) [#​33665](facebook/react#33665)) ##### React Reconciler - [createContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L255-L261) and [createHydrationContainer](https://github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L305-L312) had their parameter order adjusted after `on*` handlers to account for upcoming experimental APIs </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDguNCIsInVwZGF0ZWRJblZlciI6IjQyLjEwLjUiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> Reviewed-on: https://git.in.csmpro.ru/csmpro/csm-mapban/pulls/36 Co-authored-by: Renovate Bot <[email protected]> Co-committed-by: Renovate Bot <[email protected]>
Since the very beginning we have had the
progressiveChunkSizeoption but we never actually took advantage of it because we didn't count the bytes that we emitted. This starts counting the bytes by taking a pass over the added chunks each time a segment completes.That allows us to outline a Suspense boundary to stream in late even if it is already loaded by the time that back-pressure flow and in a
prerender. Meaning it gets inserted with script.The effect can be seen in the fixture where if you have large HTML content that can block initial paint (thanks to
rel="expect"but also nested Suspense boundaries). Before this fix, the paint would be blocked until the large content loaded. This lets us paint the fallback first in the case that the raw bytes of the content takes a while to download.You can set it to
Infinityto opt-out. E.g. if you want to ensure there's never any scripts. It's always set toInfinityinrenderToHTMLand the legacyrenderToString.One downside is that if we might choose to outline a boundary, we need to let its fallback complete.
We don't currently discount the size of the fallback but really just consider them additive even though in theory the fallback itself could also add significant size or even more than the content. It should maybe really be considered the delta but that would require us to track the size of the fallback separately which is tricky.
One problem with the current heuristic is that we just consider the size of the boundary content itself down to the next boundary. If you have a lot of small boundaries adding up, it'll never kick in. I intend to address that in a follow up.