Supporting streaming SSR in the future #4831
Replies: 8 comments 13 replies
-
I find it complicating things too... it's more prone to bugs and unexpected behavior. On the other size I find streaming as good feature for files that are typically "big". (Which in case of code spliting, is not that much problem for code). Instead of doing drastic change at once, I would probably add streaming for large files (that are not critical code, maybe for non text format files) first (we can maybe check for size before we serve it?), and then see what result we get. Maybe there could be option for it in |
Beta Was this translation helpful? Give feedback.
-
Some useful tips I was given:
|
Beta Was this translation helpful? Give feedback.
-
Another challenge to streaming is that we can't start rendering the root layout component until the leaf component's data has loaded, because the layout may depend on |
Beta Was this translation helpful? Give feedback.
-
What might make a lot more sense to implement and perhaps get even more performance gains would be HTTP 103 Early Hints. I think we could send the list of JS / CSS files we expect the page will need early on and then respond later with the SSR-rendered HTML. |
Beta Was this translation helpful? Give feedback.
-
I'm personally against the streaming API it doesn't make sense and it doesn't really make sense to use it. |
Beta Was this translation helpful? Give feedback.
-
I spent a lot of time trying to figure out how to best interoperate on this problem, so forgive me for this big comment. When is HTML streaming most useful?I worked on a site where the flow to get a response code looked like:
Streaming was most helpful right after №2. Sites where steps 2 and 3 happen at the same time (like a SQL query) would not benefit as much, but they would still benefit for data fetching like №4. Another case was search results. A search that returns 0 results is still a What status information would be helpful mid-stream?
|
Beta Was this translation helpful? Give feedback.
-
Streaming and Suspense are really the same feature. Linking to the Suspense thread. From my understanding, all streaming really does is this: NextJS (for example) seems to just load the component as usual, loading only the <!-- 1. loads the html and head from serverSideProps -->
<html>
<head>
...
</head>
<body>
<!-- 2. loads the body and any suspense fallback components, starts the async fetches in the children... -->
<Suspense fallback="loading...">
<OtherComponent />
</Suspense>
...
<!-- 3. Selectively hydrates the page, as if complete to allow interaction -->
<!-- 4. loads the script tag at the end with the data to hydrate the component(s) in suspense after fetch completes -->
<script>
// ... the js code to hydrate the "loading" with the correct info in "other component" will be here
</script>
</body>
<!-- 4. Runs the JS to Selectively hydrates again replacing fallback with actual data --> It seems to me that all it's doing is loading the hydration information last, while allowing you to interact on your page quicker. It will always wait for the head information first, so you can't really stop that. It is not good if you need to load SEO information later. It is only good for quicker user interaction. Hence, we still need load functions (like nextjs's getServerSideProps) to get our data first. It is streamed because the page does not complete until the JS info is hydrated at the end (remember before we had So, @Rich-Harris correct me if I'm wrong, but I don't think NextJS has any of the problems you mentioned, because it is not built for SEO information in the header. Technically it could help with SEO due to a higher Lighthouse score and body content, but it would have nothing to do with It seems the body gets hydrated, and then hydrated again using Selective Hydration. That way the time to interactive is faster. It is not fetching anything from the client, so it is way faster. This is all thanks to the renderToPipeableStream api that React 18 wrote from Node.js Stream. Links:
So, I don't see why this would be too difficult in SvelteKit, or at least not impossible. Just my thoughts, J Also worth adding, this is how Qwik loads its data by default when using the useResource$ function. It streams the data (confirmed by Qwik). |
Beta Was this translation helpful? Give feedback.
-
With the new stream feature, aren't we one step closer to make it work? @Rich-Harris ? This feature would be AWESOME with gRPC streaming connection, where the data can be visible to the client the moment it cames to the server :) |
Beta Was this translation helpful? Give feedback.
-
Currently, SvelteKit renders HTML in one go, which I tend to think is the better choice despite that being a minority opinion (you can't set the response status code once you've started sending the body, and you generally don't know for sure what the status code is until you've rendered the body, so AFAICT streaming implies 200-everything-and-hope-for-the-best).
It might be worth preparing for a world in which a) we want to give people the option to stream, and b) it's possible to set status codes late (which I understand is under active discussion), in which case streaming HTML makes more sense to me.
There are currently three obstacles that I'm aware of:
Svelte
Svelte itself doesn't have a streaming render mode, and without it there's not much benefit to streaming the non-Svelte parts of a page (other than optimising TTFB, I guess, which isn't a particularly meaningful achievement). That could change — we could introduce APIs that allowed us to (for example) resolve
{#await}
blocks rather than rendering the pending state:In SvelteKit, we could wrap nested routes in
{#await}
blocks that wait forload
functions to resolve, allowing us to render the nav bar before the main content is ready, etc.<svelte:head>
If we stream everything in order,
<svelte:head>
puts us back to square one — we can't render the%svelte.head%
insrc/app.html
until we've finished rendering the body and know that we won't encounter any more<svelte:head>
elements.Two options spring to mind: we somehow gather head content prior to rendering, or we stream out of order. The first option makes sense in some cases, but not all (e.g. a document title often depends on the same data as the body content), and would involve some fairly substantial design changes.
The second option would be easier (render a placeholder element to the head, and write a small
<script>
that inserts HTML adjacent to the placeholder as<svelte:head>
elements are rendered), though out-of-order streaming makes me a little nervous — it relies on JavaScript (albeit not in the inherently brittle way that 'relies on JavaScript' usually implies), and I would expect it to be significantly worse for SEO, unless we did something tricksy like defaulting to in-order streaming if the UA is a known search engine bot.transformPage
People use
transformPage
for making arbitrary tweaks to the HTML (like replacing a<html lang="%lang%">
placeholder), and the API assumes the whole page is rendered.In a streaming world this API makes no sense. We need to transform the stream instead, which in the general case is rather more complicated than something like a naive
transformPageChunk
.This is all fairly vague, because I don't have a lot of answers or strong opinions. I'm mostly putting this here so we have a place for people who do have answers and strong opinions (including about how important streaming is).
Beta Was this translation helpful? Give feedback.
All reactions