Skip to content

Avoid completely replacing the DOM when Blazor components are re-rendered after prerendering #42561

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
javiercn opened this issue Jul 4, 2022 · 33 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one Pillar: Technical Debt Priority:1 Work that is critical for the release, but we could probably ship without
Milestone

Comments

@javiercn
Copy link
Member

javiercn commented Jul 4, 2022

Right now, after prerendering we replace the entire contents of the prerendered component with a fresh DOM copy created from scratch. This has several bad side-effects like:

  • Videos are stopped and restarted
  • Animations can be retriggered
  • The page can flicker if CSS is injected as part of the head contents.
  • LCP score suffers because (even though there is no significant visual change in most cases) the tools detect the interactive DOM insertion as the LCP element.

Instead of replacing the contents of the DOM wholesale, we can do a best effort to reconciliate the DOM with the new rendered DOM when we are trying to apply the render batch.

In an ideal scenario, a developer should be able to produce exact prerenders, meaning that the render after prerendering is identical to the prerendered one.

In that case, the renderer would only need to "attach" event handlers to the appropriate nodes, leaving everything else the same.

The renderer does not have to be perfect and handle all possible scenarios, it can impose strict limitations and "bail out" if it can't figure out how to reconcile a given DOM subtree by replacing that node wholesale.

For example, the render can impose a strict order, suggesting that DOM children nodes in a given subtree must appear in the same order as the prerendered node (in other words, the renderer won't search for them, but iterate in order). A similar restriction might be imposed on attributes (which is not rare, since attributes are emitted in compilation order, except for splatting). Things like @key could be leveraged during prerendering too to offer a bit more flexibility in the ordering.

In general, this will likely result in a much higher node reuse and will give the developers the chance to always get this right.

@javiercn javiercn added area-blazor Includes: Blazor, Razor Components feature-prerendering Issues related to prerendering blazor components labels Jul 4, 2022
@javiercn javiercn added this to the .NET 7 Planning milestone Jul 4, 2022
@ghost
Copy link

ghost commented Jul 4, 2022

Thanks for contacting us.

We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@mkArtakMSFT mkArtakMSFT added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Sep 20, 2022
@mkArtakMSFT mkArtakMSFT added the Priority:1 Work that is critical for the release, but we could probably ship without label Nov 10, 2022
@3hxx
Copy link

3hxx commented Nov 21, 2022

Yes yes yes. This is long overdue my guys 🙏😍😇

@aryehsilver
Copy link

Great to see this is finally being worked on. Let's hope it doesn't get pushed off any longer.

@fingers10
Copy link

You can refer ilovedotnet for this issue. The site is built from scratch using blazor wasm and the site also facing flicker issue when it gets loaded. The site has prerendering enabled during publish time in the pipeline. When the site loads the prerendered screen appears and when the blazor engine kicks in, there comes a flash and screen appears after that.

@ghost
Copy link

ghost commented Mar 3, 2023

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@mkArtakMSFT mkArtakMSFT added this to the .NET 10 Planning milestone Jan 30, 2024
@andersme
Copy link

andersme commented May 3, 2024

This one hurts pretty badly with .NET 8 if you have SSR with any kind of interactivity somewhere

Agreed. Without making this transition seamless, SSR is pretty useless right now. This is a must-have to compete with the competitors in the market.

@andersme
Copy link

This one hurts pretty badly with .NET 8 if you have SSR with any kind of interactivity somewhere

Agreed. Without making this transition seamless, SSR is pretty useless right now. This is a must-have to compete with the competitors in the market.

And now I've simply replaced the WebAssembly interactivity with HTMX. What a cobbled mess Blazor has become.

@cbordeman
Copy link

This has been such a huge issue to so long, it kinds of blows my mind that the ASP.Net team keeps pushing it out as if it weren't important.

I agree with other commenters, after doing several Blazor sites in .Net 6, Blazor has become a cobbled mess. I switched to Blazor Server to get around WASM load speed issues, but now I wish I'd pulled the trigger on HTMX several months ago.

@fingers10
Copy link

@javiercn I'm curious to know the plans to work on this issue. It's been there for a very long time from since I started using blazor WASM from the initial release. What's the thoughts of ASP.NET team on this?

@fingers10
Copy link

This one hurts pretty badly with .NET 8 if you have SSR with any kind of interactivity somewhere

Agreed. Without making this transition seamless, SSR is pretty useless right now. This is a must-have to compete with the competitors in the market.

And now I've simply replaced the WebAssembly interactivity with HTMX. What a cobbled mess Blazor has become.

@andersme you mean you switched to plain HTML? Can you explain more? I'm also looking for alternatives now.

@MarkusRodler
Copy link
Contributor

HTMX is another competitor and is similar to Knockout.js and Angular 1. If someone wants to build websites like it's 2010, go for it 😄

@msynk
Copy link

msynk commented Mar 10, 2025

Without this feature, preventing the flicker (or reducing its effects) requires a lot of work and creativity on both the server and the client side. Since other competitors have this (smooth hydration) out of the box, it has a big impact on projects developed by Blazor.

We love Blazor and the vast opportunities it opens for .NET developers, and I hope this won't be delayed any further than the .NET 10 milestone 🤞.

@jirisykora83
Copy link

This one hurts pretty badly with .NET 8 if you have SSR with any kind of interactivity somewhere

Agreed. Without making this transition seamless, SSR is pretty useless right now. This is a must-have to compete with the competitors in the market.

And now I've simply replaced the WebAssembly interactivity with HTMX. What a cobbled mess Blazor has become.

@andersme you mean you switched to plain HTML? Can you explain more? I'm also looking for alternatives now.

basically, similar to https://alpinejs.dev/ it is fine when you need just very little interactivity and basically use Blazor as static rendering = fast loading time, no flickering etc..

but it is definitely not good tradeoff for anything other than very simple website (in terms of interactivity / front-end logic).

@oluatte
Copy link

oluatte commented Mar 11, 2025

Bummed to see the recent comments and current state on this issue. We also removed Blazor based interactivity from our app and mvoed to one of the HTMX variants (Datastar).

@mip1983
Copy link

mip1983 commented Mar 11, 2025

I've not found this to be much of an issue myself. I'd rather not hold up the server side pre-render with async database type work, just to do it again when the page goes interactive. I'd rather that initial render was as fast as possible with loading indicators in place of interactive content, which is pretty straightforward.

Either, move your async code from OnInitializedAsync to OnAfterRenderAsync inside the first render if block, or wrap it in 'if (RendererInfo.IsInteractive). Voila, your page pre-renders fast, you do your async work once, and the dynamic async content appears when it's interactive (no flicker) which is pretty quick when published. There's often not a great deal of value rendering that content before that point anyway as it tends to be in components that need interactivity anyway. (I think it can be worse to get pixels onto the screen, to then have clicks ignored because something isn't interactive yet).

If you're determined to have this stuff in the oninit/pre-render, have a look at the community stand up 'what's new for Blazor in .Net 10' on YouTube. About 40 mins in, you can see what you can do to persist state today, and what's coming. That's all overhead though, I think you'd need a pretty good reason to want to for pre-rendering. Makes a bit more sense for Blazor server and persisting state on reconnect.

@msynk
Copy link

msynk commented Mar 13, 2025

@mip1983 It's not that simple. For example, consider the SEO of dynamic data, which is a big requirement. There are other important requirements that need the prerender to work, as explained in this issue.

@mip1983
Copy link

mip1983 commented Mar 13, 2025

@mip1983 It's not that simple. For example, consider the SEO of dynamic data, which is a big requirement. There are other important requirements that need the prerender to work, as explained in this issue.

Like I said:

have a look at the community stand up 'what's new for Blazor in .Net 10' on YouTube. About 40 mins in, you can see what you can do to persist state today, and what's coming

@msynk
Copy link

msynk commented Mar 13, 2025

Like I said, it's not that simple.
no workaround can completely resolve this issue without fixing this completely replacing the DOM while hydrating.

@3hxx
Copy link

3hxx commented Mar 13, 2025

Being able to pre-render drastically improves any SEO performance as it will send a skeleton of the HTML with all of your SEO friendly wording in it to the search engine.

@mip1983
Copy link

mip1983 commented Mar 13, 2025

Like I said, it's not that simple. no workaround can completely resolve this issue without fixing this completely replacing the DOM while hydrating.

What's an example of something you can't work around at the moment? Can you not pre-render and hydrate your SEO related content this way? https://www.youtube.com/live/IXU3hbnaX50?t=2218s

@msynk
Copy link

msynk commented Mar 13, 2025

What's an example of something you can't work around at the moment? Can you not pre-render and hydrate your SEO related content this way? https://www.youtube.com/live/IXU3hbnaX50?t=2218s

As I've said, it's not that simple, so I hope I can explain it clearly.
The problem discussed in this issue is not about the simple flickering shown in the video that could easily be solved with the PersistentState which we all are already using for the prerendering of our apps.

The problem, as explained in the issue itself, is something that happens at the lower levels of the Blazor rendering while hydrating the app after initialization for the first time when receiving the pre-rendered content.
The Blazor runtime, unlike other famous frameworks out there, instead of using the already rendered content of the HTML received from the server and just attaching to it, tries to re-rerender the whole UI and replace the already rendered DOM. This approach's problem is not that noticeable on simple UIs like the one shown in the video, but in real-world applications, this is noticeable and sometimes problematic.

@3hxx
Copy link

3hxx commented Mar 13, 2025

What problems are your users experiencing? Do you have a video you can share?

@ysmoradi
Copy link

ysmoradi commented Mar 13, 2025

When a prerendered document is retrieved by the browser, CSS files begin downloading.
Once the CSS files are fully downloaded, the content becomes visible to the user.
At this point, animations start playing, users can scroll through nested items in lists, CSS-based carousels begin cycling to the next photos, and users can play any embedded videos.

While these user interface interactions are occurring, JavaScript files start downloading in the background, followed by hundreds of (WASM) files. After that, a prerendered state (encoded as a base64 JSON string located at the bottom of the document's body) is read and deserialized into the corresponding classes (The ASP.NET Core team has ensured that no System.Text source generators can be used in this process, deliberately 😅 keeping it slow)
A new DOM is then generated in memory and eventually replaces the existing DOM.

The process of downloading and executing code may take several seconds, depending on the client's network conditions, the size of the web app, and the processing power of the client's device CPU.

When this finishes, the list of items the user had scrolled through resets to the top, any video restarts from the beginning, animations replay, and the CSS-based carousel returns to the first photo. This moment is marked as the Largest Contentful Paint (LCP), even though the user could already read the content seconds earlier!

Why would you need a video to explain such an obvious scenario? @3hxx

@oluatte
Copy link

oluatte commented Mar 13, 2025

Yeah, I don't understand the recent questions asking to justify this issue. The original issue was filed by an MS employee, the description in it is clear. All the reactions above clearly indicate it is not a solitary problem. I and others have ditched Blazor interactivity or Blazor entirely because of it.

What is the skepticism here?

@mip1983
Copy link

mip1983 commented Mar 13, 2025

I'm not saying this isn't a nice thing to get solved, but for people to say it makes Blazor interactivity unusable or that you have to jump to another framework seems a bit of an over the top reaction. I'm just trying to inject some first hand experience/realism and share some work arounds and upcoming changes that will help.

I have a my own real world Blazor site infront of me. It pre-renders quickly, you get the main layout, menu, placeholder widgets and loading indicators, there's a video background splash screen on load, CSS animations into a complex interactive (wasm) dashboard with charts and carousels, all of which play once with no flicker, going from 0 to interactive in ~1s (regardless of whether I'm on my desktop or low powered mobile). If you where to read ysmoradi description, you'd think that was basically impossible or really hard to do. It isn't.

@oluatte
Copy link

oluatte commented Mar 13, 2025

@mip1983

Good for you. And your site sounds really fleshed out which is also great. I get that you're not seeing this issue but that fact by itself doesn't invalidate that others have seen this issue. I've been where you are (not seeing the issue others are seeing), and likely others have too. Never makes sense to dismiss other's experiences, scenarios, sense of urgency etc.

Hydrating pre-rendered content is something nearly every FE framework does today. It's frankly embarrassing for something like this to be status quo that requires significant workarounds to implement (assuming it can be worked around).

if you have helpful tips here or know of a given solution, we're all ears. Otherwise it's just a shite attitude.

@ysmoradi
Copy link

Let's talk with real examples. https://bitplatform.dev/ is the smallest website we've ever built using Blazor WebAssembly, with a total size of just 2.5MB. Another example is https://sales.bitplatform.dev/, which comes in at 3MB.

On a low to mid-range Android device with a 4G connection, these ultra-light websites take around 4 seconds to become interactive.

I'm guessing you're referring to a desktop setup with a powerful PC and a high-speed network. If you share your website's URL here, I can say how long it would take to become interactive on a Galaxy A35 with a 4G connection @mip1983

@GajaLab
Copy link

GajaLab commented Apr 23, 2025

I am a cms and website developer. The biggest limitation of Blazor is on web development where it is mandatory to use prerender for obvious reasons related to seo. Making the user wait a few seconds (especially on mobile) to use the interactive buttons is unacceptable. So much so that on the sites, with the exception of pages that can be rendered without prerender, such as the cart, I had to replace the interactivity with htmx.
Blazor is great for applications without prerendering, but for everything else it is not usable interactivity by today's standards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one Pillar: Technical Debt Priority:1 Work that is critical for the release, but we could probably ship without
Projects
None yet
Development

No branches or pull requests