[Complete] RFC: Incremental Hydration #57664
Replies: 17 comments 44 replies
-
Answer Question 1: No 🫠 please explain and give us some use case |
Beta Was this translation helpful? Give feedback.
-
Question (2): What would be the execution flow of the provided example with a mix of standard and hydration triggers? Syntax is not hard to read but how to reason about it (execution)
|
Beta Was this translation helpful? Give feedback.
-
I'm super happy to see this RFC, everything here makes sense. I have just one feature request: please consider adding the “time” argument to “idle”.
That would load the HeavyComponent either on hover or after a 2s delay after “idle”. There are just components that are far enough from the user’s path, and heavy enough to load them not on just “idle”, but with a small delay - because more important components will take a chance to be loaded on “idle”. Overall, this RFC is awesome and I hope it will be implemented in v19. |
Beta Was this translation helpful? Give feedback.
-
Will there be support for placeholder and loading blocks? |
Beta Was this translation helpful? Give feedback.
-
Discussion Question 1: Yes, it's clear Discussion Question 3: The syntax doesn't seem to be overly complicated. However, it is important to clearly describe the dependencies between different states, as I mentioned in point 2. Discussion Question 4: I don't have an opinion on this matter Discussion Question 5: I don't have an opinion on this matter |
Beta Was this translation helpful? Give feedback.
-
The RFC is great, good job!
Additional feedback:
|
Beta Was this translation helpful? Give feedback.
-
A little bit mixed with
Syntax makes complete sense.
No. Especially if Angular warns us about non-usable combinations, as it does with
No, but I do see common use with
I don't think so. |
Beta Was this translation helpful? Give feedback.
-
I'd want the outer component to be hydrated while keeping the ad-unit component unhydrated.
What if some of the hydration triggers could also be added to Component config, so that whenever we use the component it's default hydration settings is applied, with option to overwrite with
|
Beta Was this translation helpful? Give feedback.
-
Q2: Any reason @hydrate (on hover) {
<my-component />
} |
Beta Was this translation helpful? Give feedback.
-
I guess that I would generally use different triggers as in most cases, placeholders are short-lived as they don't make as much sense to the user as a dehydrated view which can often be hydrated way later. |
Beta Was this translation helpful? Give feedback.
-
My gut feeling is that most users would prefer the default behavior to be Is there a reason why |
Beta Was this translation helpful? Give feedback.
-
Yes. I am afraid that this can become quite confusing, especially for newcomers. Another thing that could help is something like reusable policies: // defer-policies.ts
const lowPriorityPolicy = createDeferPolicy({defer: 'idle', hydrate: 'interaction'});
// my-cmp
@Component({
template: `
@defer(policy lowPriorityPolicy) {
...
}
`
})
class MyCmp {
lowPriorityPolicy = lowPriorityPolicy
} or a better syntax if possible. The policies would of course have to be static so that the compiler can understand them. @thePunderWoman if you think this could be interesting, I can open an issue for this as it is not directly related to the RFC. |
Beta Was this translation helpful? Give feedback.
-
What if I want the whole app to be set as hydrate never and then hydrate individual child components on demand e.g. hydrate some components on interaction and some on when they enter the view port. Something like an app wide confg that makes the whole app a static app with almost zero JavaScript unless a user starts interacting with the app. |
Beta Was this translation helpful? Give feedback.
-
I use an ngx-image-cropper component, that access window, and document objects right at the start. In Angular SSR application this component throws an error: "ERROR RuntimeError: NG04002. Two solutions helped me: @defer (on immediate) { or <image-cropper ngSkipHydration With incremental hydration, what solution should I choose? |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Sounds like an amazing game 🤩 Loge to see the future of apps with Angular. This is very amazing chunk to even not load KB's of code until we interact. |
Beta Was this translation helpful? Give feedback.
-
tl;dr; thank you for all the feedback - our intention is to implement APIs described in this RFC and roll them out in developer preview for Angular v19! Shipping it as a developer preview in Angular v19!Thank you so much to all of you that left comments, questions, and feedback in this RFC. It was helpful in understanding people's impressions of this API and functionality. Summary of the discussion questions:
Mostly the answer here was "yes", but there were a few clarifying questions people asked.
The answer here is also largely "yes it makes sense", with some suggestions for a
The answer here is that the syntax is clear, but there might be confusion around the behavior between the two trigger types. We hope to address this with clear documentation.
There wasn't much response here to indicate there were concerns.
Similarly there wasn't much response to this question. I think we'll likely get more feedback as people try this feature out. Wrap upBased on all the comments and feedback we didn't find any use cases that were not covered in this design. There's some interest in a specific Thanks everyone! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Authors: @thePunderWoman
Area: Angular Framework
Posted: September 4, 2024
Status: Open
In Angular version 16, we launched full application hydration in Angular to improve your Core Web Vitals, server side rendering performance, and improve the overall initial render experience when using server side rendering. Then, in version 17, we added deferrable views, which allowed you to easily defer less important code until it was needed later, which further improved core web vitals and reduced initial bundle size. We've since also added event replay, which means none of your users' actions will be lost before hydration kicks in.
Building on this functionality, we're excited to share more concrete plans for the next SSR improvement: incremental hydration. With incremental hydration, deferred content is rendered on the server side and skipped over during client-side hydration and bootstrapping - it's left dehydrated. This allows Angular to bootstrap the initial page in less time, and keeps the code for those dehydrated components out of your initial bundle. Angular will leave those parts of the UI dehydrated until your declared hydration conditions are met, at which point it will download the necessary code and hydrate those components on demand. You already know the set of hydration triggers, as they're the same familiar
@defer
triggers:on viewport
,on interaction
, etc.Incremental hydration allows you to apply these familiar client-side
@defer
optimizations to server-side rendered code, delaying the loading of less critical parts of your UI while avoiding negative effects like content layout shifts.What kind of feedback are we looking for?
While we're excited to hear any and all comments on incremental hydration in Angular, we have additionally called out specific discussion points throughout the RFC for directed feedback. When you leave a comment, be sure to note the open question(s) to which you're responding.
As always, keep our code of conduct in mind. We know that a proposal of this scope significantly impacts Angular and the way you use it in your projects. We appreciate your commitment and investment in this project and ask to keep comments respectful.
Goals
Discussion Question 1
: Is it clear to you what is meant by "incremental hydration"?Dehydrated Content
When Angular leaves content on the page in a dehydrated state, it behaves as static HTML:
In other words, the content appears exactly as it did during server-side rendering.
Only when your hydration condition is met does Angular download the code for the components/directives within the content and hydrate it: instances are created, lifecycle hooks execute, and change detection updates the content based on its bindings.
It's possible that users could interact with your dehydrated content before it can hydrate, especially since the hydration process requires fetching code and is therefore asynchronous. Angular addresses this using the Event Dispatch library, by adding event listeners to your dehydrated content and replaying events after hydration occurs (similar to the
withEventReplay()
option for the main SSR'd application).Syntax
Deferrable views, or defer blocks, are the building (defer) block and hydration boundary for incremental hydration in Angular. A defer block becomes a incremental hydration boundary by adding a hydrate on, hydrate when, or hydrate never condition. For example:
Similar to defer triggers and prefetch triggers, you can use multiple hydrate triggers at once, and hydration will occur when any of those triggers fire.
Hydration triggers apply when a
@defer
block is server-rendered, during the application's initial load. The standard defer block triggers apply only to client side rendered@defer
blocks. Since any@defer
block might also be client-side rendered, you will need to configure a regular defer condition in addition to any hydration conditions.Discussion Question 2
: Does this syntax make sense to you? Specifically consider the difference between regular triggers and hydrate triggers. Is there an alternative that might work better?Discussion Question 3
: Is the defer syntax becoming too hard to read or understand with multiple triggering paradigms and conditions?Discussion Question 4
: Do you see yourself commonly using the same triggers for defer and hydrate? Or different conditions? For example:@defer(on interaction; hydrate on interaction)
vs@defer(on interaction; hydrate on immediate)
Trigger Types
The trigger types for incremental hydration are similar to the main @defer, with a few differences. Hydration triggers always apply to the actual rendered body of the @defer block and thus don't allow element references. They only apply to @defer blocks that are server-side rendered.
idle
idle triggers hydration once the browser has reached an idle state (see
requestIdleCallback
).interaction
hydrate on interaction
instructs Angular to hydrate content when the user interacts with it (click, focus, touch, and input events like keydown, blur, etc).immediate
hydrate on immediate
triggers the deferred load and hydration immediately.timer(x)
timer(x) triggers hydration after a specified duration.
hover
hover triggers deferred loading when the user hovers their mouse over the server rendered content. This condition is useful if hovering the mouse is a good indication that the user might soon interact with the content.
viewport
viewport triggers hydration when the server rendered content enters the viewport (detected using the
IntersectionObserver
API).never
hydrate never
tells Angular to leave your component dehydrated indefinitely, effectively treating it as static content. Note that this is not the same as a server-only component. When a@defer
block is instantiated during normal client-side rendering, the content is still fetched and rendered normally.when
Just like regular and prefetch deferrable view triggers, you can provide a custom condition which triggers hydration when it becomes
true
. There are some caveats to this, which is outlined in the nesting section.Nested Hydration
When multiple
@defer
blocks are in a dehydrated state, their conditions are applied simultaneously. For example, in the following structure:The outer block will hydrate if the mouse is hovered over any of the content inside of it. Even if the block is never hovered, the inner block's timer will trigger hydration after 15 seconds.
There is one exception to this behavior:
hydrate when
only works if the component which contains it is already hydrated. If awhen
trigger for a block is nested inside a component which is itself dehydrated, it won't be evaluated until the parent component is triggered and hydrated independently. For example:If the outer block has not triggered, then regardless of the value of
user()
, the inner block will not hydrate.When hydration is triggered within a nested block, any parent block(s) are first hydrated if necessary, and then the triggered block is hydrated. Because each hydration step requires fetching the code for dependencies, this means that nested dehydrated blocks require a waterfall of code loading in order to hydrate.
Discussion Question 5
: Do you have components you would like to use incremental hydration with but would be unable to due to the potential for stale data? Would that change if incremental hydration was triggered by inputs changing? Similarly would that change if hydration was triggered by signals changing?Common Potential Pitfalls
Root text nodes
The hydration triggers of
interaction
,hover
, andviewport
do not work with plain text nodes at the top (root) level of the deferred content, as browsers do not support listening for events on text nodes or watching them withIntersectionObserver
. Angular will therefore not allow such nodes in@defer
blocks which use those triggers. Text must be wrapped in an element (such as<p>
) in order to work correctly with incremental hydration.Frequently Asked Questions
Q: I have a question, but it's not ready yet.
…ok
Q: When will this feature be available?
When it's fully hydrated.
Q: Islands?
We're thinking about them.
Q: Does this mean Angular has server components now?
No, it does not. Server components should always render on the server and never on the client. hydrate triggers only apply to first load.
Q: When hydration is triggered on nested blocks, each with hydrate triggers, will that result in cascading requests or waterfalls?
Yes, hydrate triggers on nested components will be a waterfall of requests, but this is something we plan to address.
Q: OK, my question is ready now. Does this new version of Angular Cereal also have marshmallows?
Ah I get it, this is a hydration trigger joke…and yes…marshmallows.
Beta Was this translation helpful? Give feedback.
All reactions