Skip to content

Conversation

guardrex
Copy link
Collaborator

@guardrex guardrex commented Jul 31, 2025

Fixes #35717

Notes

  • Creates a node for State Management articles.
    • Overview: Introduction and scenarios common to server-side Blazor and Blazor WebAssembly apps.
    • Prerendered state persistence: Moves from a section of the Prerendering article to a new article in the State Management node.
    • Server: Server-side Blazor scenarios; move the Pre6 coverage on circuit state persistence from the What's New article to a section in this article. Cross-link from What's New to the coverage.
    • WebAssembly: Blazor WebAssembly scenarios.
    • Protected browser storage: Split out browser storage into its own article because its a large enough subject to merit its own article.
  • There was a bug in the Pre6 coverage that escaped Javier's review that came from Javier's example code in a PU issue discussion on circuit state preservation: The [SupplyParameterFromPersistentComponentState] attribute missing the word "parameter" in one part of the docs, which is corrected by this PR.

I'm aware of the upcoming Pre7 name change for the [SupplyParameterFromPersistentComponentState] attribute, which will be taken care of on the upcoming Pre7 docs PR.


Internal previews

Toggle expand/collapse
📄 File 🔗 Preview link
aspnetcore/blazor/call-web-api.md aspnetcore/blazor/call-web-api
aspnetcore/blazor/components/cascading-values-and-parameters.md aspnetcore/blazor/components/cascading-values-and-parameters
aspnetcore/blazor/components/data-binding.md aspnetcore/blazor/components/data-binding
aspnetcore/blazor/components/integration-hosted-webassembly.md aspnetcore/blazor/components/integration-hosted-webassembly
aspnetcore/blazor/components/integration.md aspnetcore/blazor/components/integration
aspnetcore/blazor/components/lifecycle.md aspnetcore/blazor/components/lifecycle
aspnetcore/blazor/components/prerender.md aspnetcore/blazor/components/prerender
aspnetcore/blazor/components/render-modes.md aspnetcore/blazor/components/render-modes
aspnetcore/blazor/components/rendering.md aspnetcore/blazor/components/rendering
aspnetcore/blazor/file-uploads.md aspnetcore/blazor/file-uploads
aspnetcore/blazor/fundamentals/dependency-injection.md aspnetcore/blazor/fundamentals/dependency-injection
aspnetcore/blazor/fundamentals/environments.md aspnetcore/blazor/fundamentals/environments
aspnetcore/blazor/fundamentals/routing.md aspnetcore/blazor/fundamentals/routing
aspnetcore/blazor/fundamentals/signalr.md aspnetcore/blazor/fundamentals/signalr
aspnetcore/blazor/host-and-deploy/configure-linker.md aspnetcore/blazor/host-and-deploy/configure-linker
aspnetcore/blazor/security/index.md aspnetcore/blazor/security/index
aspnetcore/blazor/security/interactive-server-side-rendering.md aspnetcore/blazor/security/interactive-server-side-rendering
aspnetcore/blazor/security/webassembly/additional-scenarios.md aspnetcore/blazor/security/webassembly/additional-scenarios
aspnetcore/blazor/state-management/index.md aspnetcore/blazor/state-management/index
aspnetcore/blazor/state-management/prerendered-state-persistence.md aspnetcore/blazor/state-management/prerendered-state-persistence
aspnetcore/blazor/state-management/protected-browser-storage.md aspnetcore/blazor/state-management/protected-browser-storage
aspnetcore/blazor/state-management/server.md aspnetcore/blazor/state-management/server
aspnetcore/blazor/state-management/webassembly.md aspnetcore/blazor/state-management/webassembly
aspnetcore/fundamentals/app-state.md aspnetcore/fundamentals/app-state
aspnetcore/fundamentals/servers/yarp/config-providers.md aspnetcore/fundamentals/servers/yarp/config-providers
aspnetcore/mvc/views/tag-helpers/built-in/persist-component-state.md aspnetcore/mvc/views/tag-helpers/built-in/persist-component-state
aspnetcore/release-notes/aspnetcore-5.0.md aspnetcore/release-notes/aspnetcore-5.0
aspnetcore/release-notes/aspnetcore-8.0.md aspnetcore/release-notes/aspnetcore-8.0
aspnetcore/toc.yml aspnetcore/toc

@guardrex guardrex self-assigned this Jul 31, 2025
@guardrex guardrex marked this pull request as ready for review August 4, 2025 14:55
@guardrex guardrex requested a review from danroth27 August 4, 2025 15:52

:::moniker-end

Prerendering might be useful for other pages that don't use `localStorage` or `sessionStorage`. To retain prerendering, defer the loading operation until the browser is connected to the circuit. The following is an example for storing a counter value:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good that we show that you can handle access storage in OnAfterRenderAsync. You could also detect whether the current render mode is interactive or not before accessing browser storage. I think that should work even in OnInitializedAsync.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Immediately after this merges, I'll open an issue on this because it's going to require an example. There's no time left to flesh this out right now 🏃.

Copy link
Member

@danroth27 danroth27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few suggestions, but otherwise this looks good to me.

@guardrex

This comment was marked as outdated.

@guardrex
Copy link
Collaborator Author

guardrex commented Aug 8, 2025

@danroth27 @javiercn ... I just reacted to all of the feedback thus far with the two exceptions for follow-up issues:

  • Prerendering content from around the repo that can move (or be duplicated) in the Prerendering article to give the Prerendering article more of an overview quality. I still think the index is helpful to finding guidance on prerendering, but I'll 🔪 it for that work when the time comes if you still don't like it.
  • A section or add-on to that existing section on "detect whether the current render mode is interactive or not before accessing browser storage." I think it will require an example, and I'm very out of time this week with Pre7 breathing down my neck 🏃‍♂️.

@guardrex

This comment was marked as outdated.

@guardrex
Copy link
Collaborator Author

guardrex commented Aug 8, 2025

Ok ... I think that fixes the 😈 .

BTW @danroth27 ... WRT the three recommendations ...

There are a three approaches that you can take to address this scenario for prerendering. The following are listed from most recommended to least recommended:

* *Recommended* for shared framework services: For shared framework services that merely aren't registered server-side in the main project, register the services in the main project, which makes them available during prerendering. For an example of this scenario, see the guidance for <xref:System.Net.Http.HttpClient> services in the [Blazor Web App external web APIs](xref:blazor/call-web-api#blazor-web-app-external-web-apis) section of the *Call web API* article.

* *Recommended* for services outside of the shared framework: Create a custom service implementation for the service on the server. Use the service normally in interactive components of the `.Client` project. For a demonstration of this approach, see the example earlier in this section.

* Create a service abstraction and create implementations for the service in the `.Client` and server projects. Register the services in each project. Inject the custom service in the component.

That came about in a discussion with Mackinnon. This is a good time to change that sequence and which ones are marked as "Recommended" 👂.

Copy link
Member

@danroth27 danroth27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're out of time, let's go ahead and get this merged. We can follow up with any feedback from @javiercn later.

* *Recommended* for services outside of the shared framework: Create a custom service implementation for the service on the server. Use the service normally in interactive components of the `.Client` project. For a demonstration of this approach, see the example earlier in this section.

Disabling enhanced navigation, which reduces performance but also avoids the problem of loading state with <xref:Microsoft.AspNetCore.Components.PersistentComponentState> for internal page requests, is covered in <xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling>.
* Create a service abstraction and create implementations for the service in the `.Client` and server projects. Register the services in each project. Inject the custom service in the component.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, this is the most correct and flexible solution. One minor tweak:

Suggested change
* Create a service abstraction and create implementations for the service in the `.Client` and server projects. Register the services in each project. Inject the custom service in the component.
* Create a service abstraction and create implementations for the service in the `.Client` and server projects. Register the services in each project. Inject the custom service abstraction in the component.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to research it back, but things have moved around several times over the years. I see where Mackinnon reviewed these back when they were moved from the Render Modes article to this article, but he didn't comment on these recommendations. I don't see a discussion there. I think I did discuss this with him, and it might have been in an email. The simplest thing to do is just figure out what they should be for an update now/soon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the available approaches are:

  1. Register the same service on both the server and client if it supports both.
  2. Make the service optional if it isn't always needed.
  3. Create a service abstraction and implement separate implementations for the server and client.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just reorganized ... possibly to the tune of what you wrote. I'll check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope ... that's not what I have. I need to re-order again. 😩

Copy link
Collaborator Author

@guardrex guardrex Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok ... the order is closer to what you said, but you left out Mackinnon's "custom service implementation for the service on the server" approach, which I now have in the last position.

The example is the one over in the Environments article ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/environments?view=aspnetcore-9.0#read-the-environment-client-side-in-a-blazor-web-app

The PR is updated now with what I think you want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's the example where we suggest implementing and injecting an IWebAssemblyHostEnvironment service for the server, which is a bit confusing given that you're not actually running on WebAssembly. But it does work, so I guess that's fine.

Copy link
Collaborator Author

@guardrex guardrex Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we reach the next issue on prerendering, this bit is going to have to be reorganized. It's a bit of a mess! 🙈😆 Each of these should be in a section with their examples. This is a case where content in another article should move, specifically the Environments example should move back to this article for sure.

Anyway ... no time right now. I have to flip over to Pre7 work.

terminator

"I'll be back." - Cyberdyne Systems T-800 Series Model 101 (Arnold Schwarzenegger)
     The Terminator 1984 Skydance Media

@guardrex guardrex merged commit 5bb40ea into main Aug 8, 2025
3 checks passed
@guardrex guardrex deleted the guardrex/blazor-circuit-state-persistence branch August 8, 2025 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Pre6 Followup Work] Blazor state persistence

3 participants