Defer Is Keeping Application Unstable, Hydration Cleanup Not Done Until Defer Finished
Problem
When using Angular's Server-Side Rendering (SSR) feature with the @defer
directive, the application can become unstable due to the hydration cleanup process not being completed until the defer is finished. This can lead to unexpected behavior and errors in the application.
Details
The issue arises when the SSR and browser-side rendering are not identical, resulting in both versions being displayed during the defer timing. This can be seen in the example provided, where the isBrowserSide
boolean is displayed as both true
and false
during the 20-second defer timing.
<div>Is Browser = {{ isBrowserSide }}</div>
@if(isBrowserSide){
<div>Inside if: browser bool = {{ isBrowserSide }}</div>
} @else{
<div>Inside else: browser bool = {{ isBrowserSide }}</div>
}
Use Case
This issue can affect applications that do not handle user authentication on the server-side and only on the browser-side. For example, a navbar that displays a "login" button when not connected and an "my account" button when connected. If a heavy component is deferred during the application loading process, both buttons will be displayed, which is not ideal.
Explanation
The hydration process waits for any deferred actions started initially to be finished before cleaning up. This is likely a design decision, and it's unclear if it's a bug. A possible solution would be to allow the defer to be run after hydration or application stability.
Workaround
A workaround without using the @defer
directive is to wait for the application to be stable and then use a setTimeout
to update a boolean to true
after a few seconds. The deferred component can then be wrapped inside an @if
directive.
Minimal Reproduction
A minimal reproduction of the bug can be found in the provided GitHub repository: https://github.com/gboutte/angular-defer-keep-unstable.git
Environment
The bug was discovered in the following environment:
Angular CLI: 18.2.14
Node: 20.18.3
Package Manager: npm 10.8.2
OS: darwin arm64
Angular: 18.2.13
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1802.14
@angular-devkit/build-angular 18.2.14
@angular-devkit/core 18.2.14
@angular-devkit/schematics 18.2.14
@angular/cli 18.2.14
@angular/ssr 18.2.14
@schematics/angular 18.2.14
rxjs 7.8.2
typescript 5.4.5
zone.js 0.14.10
Conclusion
Q: What is the @defer
directive in Angular?
A: The @defer
directive in Angular is used to defer the rendering of a component until the application is stable. This can be useful for heavy components that take a long time to load, as it allows the application to render the rest of the page while the component is loading.
Q: What is the issue with the @defer
directive?
A: The issue with the @defer
directive is that it can cause the application to become unstable due to the hydration cleanup process not being completed until the defer is finished. This can lead to unexpected behavior and errors in the application.
Q: What is hydration cleanup?
A: Hydration cleanup is the process of cleaning up the application's state after the initial rendering. This includes removing any temporary data and updating the application's state to reflect the current user interaction.
Q: Why is hydration cleanup not being completed until the defer is finished?
A: Hydration cleanup is not being completed until the defer is finished because the @defer
directive is designed to wait for any deferred actions to be completed before cleaning up the application's state. This is likely a design decision, and it's unclear if it's a bug.
Q: What is a workaround for this issue?
A: A workaround for this issue is to wait for the application to be stable and then use a setTimeout
to update a boolean to true
after a few seconds. The deferred component can then be wrapped inside an @if
directive.
Q: Can I use the @defer
directive without worrying about this issue?
A: No, it's not recommended to use the @defer
directive without understanding the potential issues it can cause. If you're experiencing issues with the @defer
directive, it's best to use a workaround or consider alternative solutions.
Q: How can I reproduce this issue?
A: You can reproduce this issue by creating a new Angular project and using the @defer
directive in a component. Then, use the isBrowser
function to display different content on the server-side and browser-side. Finally, add a @defer
directive to the component and observe the behavior.
Q: What are the system requirements for this issue?
A: The system requirements for this issue are:
- Angular CLI: 18.2.14
- Node: 20.18.3
- Package Manager: npm 10.8.2
- OS: darwin arm64
Q: Can I use this workaround in production?
A: Yes, you can use this workaround in production, but it's recommended to use a more robust solution. The workaround is a temporary fix and may not work in all scenarios.
Q: Is this issue a bug or a feature?
A: This issue is likely a design decision, and it's unclear if it's a bug. The @defer
directive is designed to wait for any deferred actions to be completed before cleaning up the application's state. However, this can lead to unexpected behavior and errors in the application.