11

Blazor .NET 8 introduces the new SSR for razor pages of "static" character. This is nice for login, status page or other non-interactive pages. On the downside it seems that having pure SSR pages forces the layouts to be non-interactive as well. In other words, I cannot have C# frontend code (interaction) on the layouts (MainLayout.razor etc). If I want that, I neeed to move both the MainLayout.razor AND the Routes.razor to the Client project and set their rendermode to InteractiveAuto. However, then I cannot render pure SSR pages any more, since routing is taken over completely by the client logic (Blazor Server/WASM)

Is there a way to combine the best of both worlds? I'd like to have a few static pages rendered only by SSR, while most of the other pages require an interactive layout with widgets etc.

Essentially the Router should always be rendered server-side, while the layout's rendermode should depend on the scenario:

  • Server-side layout for static pages
  • interactive client-side layout for interactive pages

Is this possible?

5
  • In general you'd only do SSR to let certain bots get the content from a single URL. As is, it's working as it should (first page static, then let client-side handle events). For web crawlers, all the major ones would support crawling the site even if single-page. Commented Dec 22, 2023 at 19:05
  • 1
    I am also experiencing similar issues Commented Dec 23, 2023 at 15:45
  • I've been working on this issue on and off for a little while now. I've put together similar multi-site hosting for previous versions of Blazor, so am interested to do the same with the new render modes. I would guess I'm 80% of the way there, but that last 20% at the moment is proving illusive. Commented Dec 23, 2023 at 17:33
  • Based on your scenario and description, you can conditionally render layouts based on certain criteria. For example, you might check a property in your component that indicates whether it's an interactive or static page, and then choose the appropriate layout. Let's say, you can create StaticLayout.razor for static SSR pages and InteractiveLayout.razor for interactive pages. Heve you tried this way? Commented Dec 25, 2023 at 9:18
  • Hi Marco, You said you solved this..? Can I ask how as I am facing the same issue. 👍 Commented Apr 8, 2024 at 13:12

3 Answers 3

5

Layouts can be placed on both the server and client side, and each page can choose which layout it wants to use. SSR pages on the server can use layouts on the client. As far as I know (AFAIK), SSR pages are just interactive components/pages minus events, i.e., you have the full component life cycle and properties/methods, etc.

I have not found what I would call a "have your cake and eat it" mode after spending the last four days trying God knows how many different combinations of settings and component locations.

Given your comments, my assumption is that you would like pretty much the old client Web Assembly project with server-side API and a few SSR pages. If that is the case, then yes, this is possible, but there are a couple of gotchas to take into account.

Take a quick look at the new project templates and select the Web Assembly/global project with individual accounts (just for pointers). This will create SSR pages on the server, with a router and other stuff on the client. The magic for this is in the App.razor file on the server:

private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account") ? null : InteractiveWebAssembly;

A null render mode means it's an SSR page.

Now, create your own Web Assembly/global project without the account stuff and alter the App.razor so you can have WASM with SSR pages; it all works fine. Now for the quirks.

  1. You are on your WASM pages, navigating with or without the internet; all is good; it's cached on the client side.
  2. You navigate to an SSR page; it goes to the server, and the App.Razor server page is run, and then your SSR page; all is well.

The quirks/gotchas:

  1. You navigate to a WASM page, it goes back to the server and does the whole prerender thing and then runs on the client again. I have not found a way to stop this behaviour; I do not know why it needs to do this.

4a. You are on the SSR page, and you lose the internet connection; you now cannot navigate anywhere; you are just stuck on the SSR page until the internet comes back.

4b. You lose the internet while on WASM (no problem); you navigate to the SSR page and get the "You're not connected page"; you then try to go back to your cached WASM pages; nothing works; you are stuck on the not connected page until the internet comes back.

4b is not too much of an issue; just check for the internet before navigation. 4a not sure how to solve this one, and 3 just bugs the hell out of me.

All of the new project template selections/types have varying degrees of quirks and gotchas.

I have spent the last year and a half with the old-style WASM and API server path (with gRPC), no prerendering or Blazor server, without problem.

The new auto per component project is very appealing, but it is no silver bullet. It's the route I will probably go unless requirements dictate otherwise. But the last four days spent banging my head against a brick wall with the varying options and gotchas have shown me what code-arounds I would need to put in place to make things work the way I want (excluding the fetch to the server even with navigating between WASM pages). So, I am prepared if I do decide to go that route.

Please post your findings. I, for one, would be grateful for any additional findings by others, so I know more about what I would be facing depending on the project selection.

Hope my findings are of some use.

Sign up to request clarification or add additional context in comments.

5 Comments

I was able to get it working even with InteractiveAuto on the server project. So now I can combine pure SSR pages with interactive pages (auto = server/wasm), and I can have interactivity on my Layouts! Very nice!
Glad you got it working how you wanted it to work. From all of my tests basically if you want to have any sort of mixed render modes you just need to accept the minor issues that come with interactive auto per page/component (like you have done). Regarding a more pure WASM with SSR approach, that really is a no go see: github.com/dotnet/aspnetcore/issues/53130
@Marco - how did you get this going ? Maybe answer your own question?
@BrettJB it's a bit too complicated for a SO post. Let me see if I have time to create a github examle and then link it here.
Thank you. I think I might have solved it... trial and error using lots of template variations. The final trick was setting the per page interactivity to global.
2

I do no have points to allow a comment so I have had to add another answer to clarify a couple of things for @epoch and the OP.

As far as I know (currently), if you want to have mixed modes (SSR and WASM, SSR, WASM and Server), then auto per component/page project (currently) is most likely the best way to go, with the router on the server side. Again, as far as I understand, route discovery is determined by AddAdditionalAssemblies(typeof(Client._Imports).Assembly) in the server program.cs file and from the router, either on the server or client.

Currently (it may change, or it may be bugs, I do not know), if you have the router on the client and, say, a server interactive page on the server, going from the client to server runs the server component/page; it displays, then immediately goes to the 'page not found' text. Ironically, having the server component on the client works fine.

Other quirks: when you have mixed interactive components, if there is a WebSocket open to a server page/component and you navigate to a client component (that is cached on the client and has run on the client), if the WebSocket is open, the interactive WebAssembly component will run as a server component unless the prerender for the WebAssembly component has been set to false.

Regarding my first post, as it was probably not clear and you should know if you go that route, global WASM with SSR (the Blazor template/code) is that when I pointed out going from WASM to SSR and back to WASM, the call goes to the server and then runs on the client, I should have said RE-RUNS on the client, i.e., the client-side program.cs is re-run despite it already being on run and cached. I have not checked if this is just a debug runtime behavior (or bug) or happens after publishing; I suspect it is both.

This behavior may be fine for some apps/sites where there is not a lot of transition from SSR to WASM. Another thing to check, if going this route, would be client-side local browser session cache (if you use it for, say, storing validation rules); I have not checked yet, would this get wiped out or remain as you have not changed tabs?

My final thoughts and just my opinions based on my usages of Blazor to date: Blazor WASM with Server API works great (excluding initial download); the new auto per component works well if you embrace the quirks (bugs?) and you do not need the app to work (for prolonged periods) without the internet. If you embrace the new way, just remember that any component running on the client that you may want prerendering will require a couple of extra steps, i.e., a shared interface that you can register on both the client and server to provide the concrete implementations of code for when the code first runs on the server and then subsequently runs on the client (plenty of posts on that). And the code if you want to share/download the prerendered state (again lots of posts on that as well). Or turn off prerendering for the component.

Sorry for the additional post, but hope it helps.

1 Comment

Given some of my findings especially surrounding not being able to get back to client side cached pages during loss of an internet connection I raised an issue on the github aspnecore/ blazor repo. It may provide additional information for this SO post: github.com/dotnet/aspnetcore/issues/53130
0

I've run into this recently as well starting from an Interactive Auto template with per page/component interactivity. code-dispenser's suggestion will probably suffice for my own needs. I was originally taking the same approach as OP with having the router and layout on the server but ran into the issue described here:

https://github.com/dotnet/aspnetcore/issues/50518

With the router on the server, there just doesn't seem to be a way to get the @Body content down to the WASM components on the client. I tried placing a PortalLayout.razor (in the client project, interactivewebassembly rendermode) layout component with its own layout pointing to another MainLayout.razor layout component (also in the client project, no render mode specified) that simply emitted the PortalLayout component, but the @body content of the page would not show up inside PortalLayout.razor.

It'll be interesting to see if a solution for this surfaces.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.