Skip to content
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

Clarify the names of the interactive render modes for Blazor and rationalize with the component tag helper #50636

Closed
danroth27 opened this issue Sep 11, 2023 · 25 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
Milestone

Comments

@danroth27
Copy link
Member

In .NET 8 we have the following render modes for Blazor components:

  • Static: Static server rendering.
  • Server: Interactive server rendering using the Blazor Server hosting model. Prerendering is enabled by default but can be disabled.
  • WebAssembly: Interactive client rendering using the Blazor WebAssembly hosting model. Prerendering is enabled by default but can be disabled.
  • Auto: Interactive client rendering that starts with Server and switches to WebAssembly once the runtime has been downloaded. Prerendering is enabled by default but can be disabled.

We also have the existing component tag helper that defines its own RenderModes enum:

  • Static: Static server rendering.
  • Server: Interactive server rendering using the Blazor Server hosting model. No prerendering.
  • ServerPrerendered: Interactive server rendering using the Blazor Server hosting model with prerendering.
  • WebAssembly: Interactive client rendering using the Blazor WebAssembly hosting model. No prerendering.
  • WebAssemblyPrerendered: Interactive client rendering using the Blazor WebAssembly hosting model with prerendering.

One problem with the current naming scheme is that "Server" is a bit ambiguous and confusing. Both the Static and Server render modes render from the server, so it's not immediately clear what the "Server" render mode means.

We could clarify this by renaming Server to something more specific, like InteractiveServer. If we do that, we presumably should to rename the other interactive render modes for consistency:

  • Server -> InteractiveServer
  • WebAssembly -> InteractiveWebAssembly
  • Auto -> InteractiveAuto

Note that this also impacts the APIs used to configure and enable these render modes: AddServerComponents() -> AddInteractiveServerComponents(), etc.

The RenderMode enum used by the component tag helper has already shipped. If we change names of the new render modes, then they will be inconsistent with the existing enum. So, we need to decide if we care more about clarity with the new API or consistency with the existing ones. The new names are also more verbose.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Sep 11, 2023
@danroth27
Copy link
Member Author

We will also need to evaluate the Razor compiler impact if we decide to make this change.

@chsienki FYI.

@marinasundstrom
Copy link

This is a reasonable step towards clearing up the confusion.

Blazor is no longer just about interactive components, although that is the full power of it - with Blazor Web App being server static by default. It is, with lack of a better word, a universal Web component model.

I think there is need for some great docs explaining the different hosting models and render modes from this new perspective which even I find hard to define.

@danroth27
Copy link
Member Author

I think there is need for some great docs explaining the different hosting models and render modes from this new perspective which even I find hard to define.

@marinasundstrom See dotnet/AspNetCore.Docs#30279 for our initial attend to get the new Blazor render modes documented and aligned with our existing concepts. This issue was in part inspired by that doc writing effort.

@chsienki
Copy link
Contributor

We will also need to evaluate the Razor compiler impact if we decide to make this change.

@chsienki FYI.

Thanks for the heads up. I don't think this should impact the compiler; we don't directly rely on the names of the enum, just the underlying interface they represent, so any changes should be transparent to the compiler.

@SteveSandersonMS
Copy link
Member

The RenderMode enum used by the component tag helper has already shipped. If we change names of the new render modes, then they will be inconsistent with the existing enum.

True, but not a big issue in my opinion, since the older RenderMode enum is already inconsistent with the existing newer names. For example, it has no equivalent to Auto, and it does have both Server and ServerPrerendered as separate things (whereas the newer schema represents them as a single type with a flag).

Altogether the older RenderMode enum should be decreasing in importance and eventually becomes a back-compat thing. I'm fine with the new type hierarchy being optimized for the way we see things now.

@SteveSandersonMS
Copy link
Member

The new names are also more verbose.

I agree that's a real tradeoff. It's obvious that "interactive server mode" is massively clearer than "server mode" for newcomers, so the remaining question is whether it's so verbose that it's actively annoying to more experienced devs. My sense is that it's not annoying at all.

@danroth27
Copy link
Member Author

OK, it sounds like we should move forward with this then.

Altogether the older RenderMode enum should be decreasing in importance and eventually becomes a back-compat thing

I don't think the component tag helper is just for back compat. We have lots of MVC & Razor Page developers that may want to embed Blazor components in their existing apps.

For example, it has no equivalent to Auto

I was actually going to open an issue about the possibility of adding it 😄.

@javiercn javiercn added this to the 8.0-rc2 milestone Sep 12, 2023
@victor-borges
Copy link

Hey all, just chiming in!

A lot of people commented on the naming of the RenderMode enum in the recent ASP.NET Core Community Standup (myself included) and how it feels like it has a confusing meaning.

My suggestion would be to separate interactivity from (pre)rendering, as the two concepts play together but are quite different and are mostly two independent choices that one can make. In my mind, that would imply renaming the RenderMode enum to something like InteractiveMode, with the values:

  • None: meaning no interactivity at all, default if not specified, explicitly make the component static and pre-rendered;
  • Client: meaning client interactivity, same as the current Blazor WebAssembly render mode;
  • Server: meaning server interactivity, same as the current Blazor Server render mode;
  • Auto: same as the current Auto render mode;

And then the pre-render flag would also turn into a directive or an enum, that states if the component should be pre-rendered.

Pre-rendered component with server based interactivity:

@interactivemode Server
@prerendered

<h1>My component</h1>

@code {
    // some code
}

Calling a pre-rendered component with client based interactivity:

<Counter @interactivemode="Client" @prerendered />

Component that explicitly sets no interactivity (could also serve as a workaround for enabling interactivity globally in the root component):

@interactivemode None
@* Using prerendered here would be a NOP *@

<h1>My component</h1>

But I don't know if that would be possible due to backwards compatibility or other constraints that I missed, so please take it like a very surface level suggestion.

@gragra33
Copy link

@SteveSandersonMS
@danroth27 mentioned the old enum. If you added only @interactivemode None, you could keep the old enums and have the opt-in to static-only rendering using the none. This reduces confusion with a change and would keep backward compatibility with the default behaviour of .Net 7.0 & earlier. No need for changing/depreciating the old tag halper ... just a thought.

@SteveSandersonMS
Copy link
Member

Thanks for the suggestions. At this point coming into RC2 we need to avoid compiler-impacting changes, or other changes that would force large amounts of framework API churn, so we're really only considering direct renames of what already exists. We have enough consensus to proceed with InteractiveServer etc, so that remains the plan. But again, thanks for the feedback - at other times we might have acted on it :)

@gragra33
Copy link

gragra33 commented Sep 14, 2023

Thanks for the suggestions.

Totally understand. :)

@akorchev
Copy link

Hi guys! Could you shed some light why you picked static mode as the default? Do you plan to keep it that way? Won't it hurt developer productivity as people would have to always check whether @attribute [RenderModeServer] is present or not. I just spent 15 minutes wondering why a click handler did not fire in the default Home.razor component - of course it was missing the dreaded attribute.

We (the maintainers of Radzen.Blazor) already started receiving bug reports that components are not working in .NET 8 Blazor apps so I wanted to ask what the rationale for this breaking (IMHO) behavior was.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Sep 15, 2023

Yes, the high-level goal for Blazor in .NET 8 is to make it a first-class choice for a wider range of web UI scenarios before. In the past, Blazor could only be used for SPA-like apps, either with Server or WebAssembly hosting. Both of those hosting models are great in some cases and limiting in other cases. In particular, neither are ideal for high-traffic, read-only public websites where you simply want to generate and return some stateless HTML as fast and cheaply as possible.

So with .NET 8 we're following a similar architectural pattern that's become mainstream in other technologies in the last year (e.g., Next.js, Nuxt, SvelteKit, and others) whereby components by default are executed statelessly on the server, and hence do produce static HTML extremely quickly and with no hosting overhead from WebSocket connections or requiring the end user to download a .NET WebAssembly runtime. For cases where apps only need to produce HTML with forms (that can be posted back to the server), links, etc., you can now do so with Blazor's modern component programming model as an alterative to Razor Pages, MVC views, etc.

Then, it becomes more powerful still in cases where people do want to enable stateful interactivity on particular parts of the site, as it's possible to tag individual pages or components with a chosen rendermode. And we've also made it possible to mix Server and WebAssembly in the same app, and added Auto which dynamically switches to maximize startup speed and minimize hosting cost. For example, this is likely to be something people would do on an "admin" or "control panel" part of a site. This isn't a breaking change (as we define it) because existing apps continue to work the same, and we've not withdrawn any functionality. We've just added a new, additional way to run a Blazor app, which has different strengths and limitations.

Of course, full end-to-end SPA apps remain a central scenario for Blazor, either with Server or WebAssembly hosting. So we've just updated the project template to have a one-click way to get a more traditional Blazor app that's interactive from the root level down, i.e., so every page and component is interactive. This means you don't get stateless HTML generation but you do get stateful event support. It's up to developers to choose what best matches their scenarios.

We've been talking about this heavily from the beginning of .NET 8 and there are dozens of blog posts, videos, etc about it. I know not every developer in the Blazor world will have been reached by these yet, and everyone who starts with it will have some shifts of mental model to deal with (except if they just use global interactivity as mentioned above). The overall payoff is being able to use the same programming model, and often the same components, for super high-scale public HTML sites as well as for rich interactive SPAs, even in the same site. Hope that makes sense.

In terms of why the default is stateless:

  • It's not always. You can use the project template option to have global interactivity, or you can manually mark a root component as interactive.
  • But if you want stateless at all, it only makes sense for that to be the default. Because if you have any interactive components in your hierarchy, then your whole page has to pay for the overhead of that. It's most useful for developers to make components stateless by default unless they actually need state, otherwise they would never get into a situation where they could benefit from static server rendering.

We also know we need to provide more detail and guidance for component authors on how to make the best use of new capabilities. This is tracked at #47624 and should be done as part of finalizing .NET 8.

@akorchev
Copy link

akorchev commented Sep 15, 2023

Thank you for the elaborate response @SteveSandersonMS. I appreciate the insights.

I fully agree with the direction you have taken and see the value of static mode. I just wish it weren't the default as I see it causes confusion (in me and other fellow Blazor developers). All the Blazor applications I've seen (hundreds) were stateful and every component had some sort of interaction. I guess we just have to adapt with the new state of matters.

A few followup questions:

  1. I see that existing Blazor Server and WASM apps work as they used to with .NET 8. However when I do dotnet new blazorserver or dotnet new blazorwasm it creates .NET 7 apps which probably means it is using the .NET 7 templates. Do you plan to update those templates to .NET 8 or are they "deprecated" in favor of dotnet new blazor? It could be the latter considering the .NET 8 Blazor documentation which only mentions the new mode.
  2. What is the migration path for existing Blazor applications? Is it to add @attribute [RenderMode*] to every component? Or <Routes @rendermode="@RenderMode.Server" />

@SteveSandersonMS
Copy link
Member

  1. Yes, the new .NET 8 project template is simply called blazor (as in, dotnet new blazor). If you continue using blazorserver or blazorwasm, you're actually using the .NET 7 SDK. If your machine didn't have .NET 7 on it, you wouldn't have blazorserver or blazorwasm templates.
  2. We'll provide detailed migration guidance as part of finalizing .NET 8. People don't have to add a rendermode on every component as they can simply add one at the app root level (it applies hierarchically). So yes, adding it to the component containing the router should do. There are some other changes needed in Program.cs too. For now it's possible to figure that out manually by comparing against the new project template output, but we will document it.

@akorchev
Copy link

akorchev commented Sep 15, 2023

Thanks!

Yes, the new .NET 8 project template is simply called blazor (as in, dotnet new blazor)

Does it mean that the "older" SPA Blazor apps are now gone from the template or are they behind some option which is not yet implemented in dotnet new blazor?

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Sep 15, 2023

In .NET 8 there is:

  • dotnet new blazor which produces both SPA-style and static-style apps depending on flags, all hosted on ASP.NET Core
  • dotnet new blazorwasm which produces pure standalone WebAssembly SPA sites that don't involve any ASP.NET Core server, and could be deployed to a static-file host like GitHub Pages.

Sorry I was wrong above when I said that .NET 8 doesn't contain the blazorwasm template - it does.

mkArtakMSFT pushed a commit that referenced this issue Sep 15, 2023
…des. (#50684)

Fixes #50433 (Add root level interactivity option)
Fixes #50646 (Remove workaround for Counter component)
Fixes #50636 (Clarify the names of the interactive render modes)

In terms of the code we now emit, there should be nothing controversial here. The template just has to do quite a bit of if/else in many places to account for all these options and how rendermodes are used and not used based on them.

The PR is big because the renames have really wide impact, but almost all the "files changes" are just due to renames. The only real code changes are in the project templates.

# Testing impact

Adding this option, the BlazorWeb template now has **so many** possible combinations of options, including:

 - Whether or not to enable Server interactivity
 - Whether or not to enable WebAssembly interactivity
 - Whether or not to be interactive from the root
 - Whether or not to include sample content
 - Whether or not to use ProgramMain

So that's around 32 combinations of output - without even accounting for auth options! We don't currently have E2E verification of any of them, as those tests are skipped due to unreliability. We're going to have to lean hard on CTI validations for this, and make sure all the important combinations are covered - cc @mkArtakMSFT.

# Options design update

Having a list of 6 separate checkboxes in VS is pretty unpleasant and hard to understand:

<img src="https://github.com/dotnet/aspnetcore/assets/1101362/93713e83-0793-4140-82e1-95ca63580e3d" width="500" />

So, in this PR I'm proposing (and have implemented, but we can still change it), a change to use dropdowns for the interactivity type and location options. This reduces the number of inputs by one, and means they can be more self-describing:

<img src="https://github.com/dotnet/aspnetcore/assets/1101362/649c93fd-d464-499c-b1f2-36436ebf4e3c" width="500" />

 * The "interactivity type" choices are:
   * **None**
   * **Server** (default)
   * **WebAssembly**
   * **Auto (Server and WebAssembly)**.
 * The "interactivity location" choices are:
   * **Per page/component** (default)
   * **Global**

Note that "interactivity location" is disabled if interactivity type == "None", but [only CLI honors that right now](dotnet/templating#5648) (VS should add support later, and until then, location will have no effect if there's no interactivity).

I think this is much easier to understand, since you no longer have to infer that enabling both Server and WebAssembly means you're going to get Auto. It's also much better in the CLI, since it was completely ridiculous before that `--use-server` defaulted to true but `--use-wasm` defaulted to false, so to get WebAssembly you needed to set `--use-server false --use wasm`. Now you would say `--interactivity webassembly` (and not `wasm` - that was weird too).

![image](https://github.com/dotnet/aspnetcore/assets/1101362/0b4751ad-f91b-4bac-8edf-9e31aa761fbf)
@akorchev
Copy link

What is the flag which makes dotnet new blazor produce SPA-style apps? Both --use-wasm and --use-server seem create apps that are static by default unless I am missing something.

@SteveSandersonMS
Copy link
Member

We only just merged the project template update that enables this: #50684. The flag is --all-interactive.

@akorchev
Copy link

Thank you for the clarification. I got it now.

@SteveSandersonMS
Copy link
Member

Done in #50684

@danroth27 danroth27 added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Oct 4, 2023
@EmilAlipiev
Copy link

I have just upgraded to RC2 and there is something funny. how should this be working? Documentation and usage doesnt match. Whys Server mode is called Interactive and Webassembly mode is without?
This is even crashing.

image

@SteveSandersonMS
Copy link
Member

@EmilAlipiev Looks like you're using mismatched package versions. When we updated the names, we did so consistently. The new API is AddInteractiveWebAssemblyRenderMode.

@EmilAlipiev
Copy link

@SteveSandersonMS thanks for the hint. After cleaning bin obj rebuild etc. exact error was displayed. Probably this was asked before but I am curious why needed to name it "Interactive" explicitly? is it not always interactive if not static rendering?

@danroth27
Copy link
Member Author

@SteveSandersonMS thanks for the hint. After cleaning bin obj rebuild etc. exact error was displayed. Probably this was asked before but I am curious why needed to name it "Interactive" explicitly? is it not always interactive if not static rendering?

Blazor has two server-side rendering modes: static and interactive. So just using "Server" for interactive server rendering was ambiguous. We added the "Interactive" prefix to make the distinction clear. And once we did that for interactive server rendering we decided it made sense to do for all the interactive render modes.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one
Projects
None yet
Development

No branches or pull requests

9 participants