You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Cache Components] Faster partial hydration in PPR resumes (#82742)
This PR fixes some HTML/RSC stream interleaving logic that, under some
circumstances, resulted in hydration of a resumed page being delayed
unnecessarily.
Fixes NAR-284
---
When SSRing a page, we're mixing two streams -- the HTML stream, and the
RSC stream (containing scripts with RSC data, used for hydration). In a
regular (non-PPR) render, we need to wait for react to flush the first
HTML chunk (containing `<html><body>...`) before we can send the first
RSC script. This was implemented in `createMergedTransformStream`.
The problem was that `createMergedTransformStream` was also re-used when
rendering a PPR `resume()`. In that case, we're only outputting the
dynamic portion of HTML, and the shell is sent separately (either by the
next server, outside of `app-render`, or by infrastructure), meaning
that is _not_ part of the stream that `createMergedTransformStream`
receives. But the "wait for the first html chunk" logic was still there,
and thus, we were accidentally delaying sending any RSC scripts until
the first piece of dynamic HTML was rendered even if they had no
connection to each other. In particular, this also prevented us from
sending hydration data for the static shell.
There's one edge case here -- if we didn't produce a static shell (e.g.
for suspense-above-body), we should still apply the html-waiting logic,
otherwise we'd end up sending scripts before the body is rendered. But
if we have a shell, we shouldn't wait for the HTML at all.
This is now solved as follows:
1. During the prerender, we track whether or not a shell (aka "prelude")
was produced, and store that information in the postponed state object
2. When resuming, we check whether the prerender had a shell, and if it
does, we don't wait for HTML (under the assumption that it was already
sent separately, outside the dynamic render)
To test this, I've had to extend our testing setup a bit -- with the
default settings, playwright would wait until `load` is fired, which
seems to fire after all the HTML finished streaming and thus doesn't let
us inspect whether partial hydration is working correctly. We're also
waiting for `load` in `elementByCss` (apparently, for compatibility with
tests written before playwright) so i've had to work around that as
well.
0 commit comments