Replies: 8 comments 14 replies
-
Very cool, I don't know if you saw it but there's an old similar idea here: #116 |
Beta Was this translation helpful? Give feedback.
-
Very much thinking out loud so don't take this comment to mean a whole like:
I still like this idea a lot, it "fits" with Astro in a clean way architecturally and I think would make sense to users. |
Beta Was this translation helpful? Give feedback.
-
Love this proposal! Would be awesome to enable this. Fallbacks are tricky with the way Astro currently works, but we do control the DSL and could add support for components passed as props if that would be the best possible syntax. We also (quietly) support |
Beta Was this translation helpful? Give feedback.
-
I've updated the proposal, based on our discussion in Discord. |
Beta Was this translation helpful? Give feedback.
-
I wasn't part of the discord discussion, could you please add a section for why an additional network request is preferred over streaming the server islands in? |
Beta Was this translation helpful? Give feedback.
-
I wonder if Speaking of the component, would it make sense for a component to declare itself server-deferred? Is there ever a component that would break unless it was deferred? I don't think there would be, aside from some of our current error messages might be confusing vs. "hey, you forgot to server defer this" |
Beta Was this translation helpful? Give feedback.
-
This proposal has been accepted as is now stage 2: #945 Please continue the discussion there. |
Beta Was this translation helpful? Give feedback.
-
Can I use deferred with React component? |
Beta Was this translation helpful? Give feedback.
-
Summary
Allow islands within a prerendered page to be server-rendered at runtime.
Background & Motivation
Often a page may be mostly static, but with only parts that need to be rendered on-demand. For example, a product page might need to show stock levels or personalised recommendations. Currently the only option for these is to either render the whole page on demand, or to render the dynamic parts on the client. This proposal introduces the concept of deferred islands, which are not prerendered, but rather server rendered on demand at runtime.
Next.js is working on a solution called partial pre-rendering, which allows most of the page to be prerendered, with individual postponed parts rendered using SSR on-demand. The implementation is quite different from what I propose for Astro, but the concept is similar.
Goals
Possible goals
Non-goals
Example
A component would be deferred by setting the
server:defer
directive.The
"fallback"
slot can be used to specify a placeholder that is pre-rendered and displayed while the component is loading.The page can pass props to the component like normal, and these are available when rendering the component:
The component itself does not need to do anything to support deferred rendering, so it should work with any existing component. However deferred components can optionally use special powers, and can detect if they were deferred by checking the
Astro.deferred
prop. This means that it was deferred at build time but is now being rendered on-demand.The special powers are because during deferred rendering, a component is rendered like a mini page. This means it can use features such as
Astro.cookies
, and setting headers onAstro.response
. TheAstro.url
andAstro.request.url
are from the original page, and are passed in the request along with the props.Implementation
When rendering the static page, postponed elements would not be rendered, and instead would render an
<astro-island>
containing any placeholder. The<astro-island>
would embed the serialized props, as well as the URL for the deferred endpoint.When the page has loaded, a request would be made to each deferred endpoint (see "GET vs POST" below for considerations). This request would pass all of the props and other serialized context.
On the server, the component would be rendered effectively in a thin wrapper page that decoded and forwarded the props, and rewrites the Astro global values.
When the browser has loaded the response from the endpoint, it would use it to replace the content of the island.
The runtime for replacing the deferred islands would be in an inline script tag. A simplified version without error handling could look like this:
GET vs POST
One of the unanswered questions is whether to use GET or POST requests for the deferred component endpoints. The benefit of the POST is that it can send arbitrarily large request bodies. The benefits of GET are that they are cacheable and can be preloaded in the page head. Some options are:
Why not streaming?
A alternative approach would be to send the postponed components in the same response as the initial shell. This is how Next.js PPR is currently implemented. This has some benefits, but I think they are outweighed by drawbacks in most cases. Astro has always been static-first, and I think that approach is best here too.
Primarily, a prerendered, static page is easily cacheable, both in the browser but also in a CDN. This is not the case when the deferred data is in the same response. The benefits of this are that the static part can be cached at the edge, near to users, with a very fast response time. The deferred content can be rendered and served near to the site's data without blocking rendering of the rest of the page. If you want to stream the deferred content in the same response, you either have to render everything at the origin and take the hit on distance from users, or render it all at the edge and take the hit on distance from your data. In some cases rendering everything at the edge is fine (e.g. if there's no central data source or API access), and Astro already supports that.
You can work around this with logic at the edge to combine a local cached shell and a stream of the updates from the origin, and edge middleware to do this could be a helpful option. It still prevents the use of the browser cache though, because it can't make conditional requests for the prerendered page - the whole things needs to be sent on every request in case the deferred data has changed.
Beta Was this translation helpful? Give feedback.
All reactions