Skip to content

Commit 78fee7d

Browse files
authored
RCLs and static SSR article (#31090)
1 parent 630b945 commit 78fee7d

File tree

6 files changed

+173
-15
lines changed

6 files changed

+173
-15
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
title: ASP.NET Core Razor class libraries (RCLs) with static server-side rendering (static SSR)
3+
author: guardrex
4+
description: Learn how component authors can support static server-side rendering (static SSR) in ASP.NET Core Razor class libraries (RCLs).
5+
monikerRange: '>= aspnetcore-8.0'
6+
ms.author: riande
7+
ms.custom: mvc
8+
ms.date: 11/21/2023
9+
uid: blazor/components/class-libraries-with-static-ssr
10+
---
11+
# ASP.NET Core Razor class libraries (RCLs) with static server-side rendering (static SSR)
12+
13+
<!-- UPDATE 9.0 Activate after release and INCLUDE is updated
14+
15+
[!INCLUDE[](~/includes/not-latest-version.md)]
16+
17+
-->
18+
19+
This article provides guidance for component library authors considering support for static server-side rendering (static SSR).
20+
21+
Blazor encourages the development of an ecosystem of open-source and commercial component libraries, formally called *Razor class libraries (RCLs)*. Developers might also create reusable components for sharing components privately across apps within their own companies. Ideally, components are developed for compatibility with as many hosting models and rendering modes as possible. Static SSR introduces additional restrictions that can be more challenging to support than interactive rendering modes.
22+
23+
## Understand the capabilities and restrictions of Static SSR
24+
25+
Static SSR is a mode in which components run when the server handles an incoming HTTP request. Blazor renders the component as HTML, which is included in the response. Once the response is sent, the server-side component and renderer state is discarded, so all that remains is HTML in the browser.
26+
27+
The benefit of this mode is cheaper, more scalable hosting, because no ongoing server resources are required to hold component state, no ongoing connection must be maintained between browser and server, and no WebAssembly payload is required in the browser.
28+
29+
By default, all existing components can still be used with static SSR. However, the cost of this mode is that event handlers, such as `@onclick`&dagger;, can't be run for the following reasons:
30+
31+
* There's no .NET code in the browser to run them.
32+
* The server has immediately discarded any component and renderer state that would be needed to execute event handlers or to rerender the same component instances.
33+
34+
&dagger;There's a special exception for the `@onsubmit` event handler for forms, which is always functional, regardless of render mode.
35+
36+
This is equivalent to how components behave during [prerendering](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), before a Blazor circuit or Blazor WebAssembly runtime is started.
37+
38+
For components whose only role is to produce read-only DOM content, these behaviors for static SSR are completely sufficient. However, library authors must consider what approach to take when including interactive components in their libraries.
39+
40+
## Options for component authors
41+
42+
There are three main approaches:
43+
44+
* **Don't use interactive behaviors** (Basic)
45+
46+
For components whose only role is to produce read-only DOM content, the developer isn't required to take any special action. These components naturally work with any render mode.
47+
48+
Examples:
49+
50+
* A "user card" component that loads data corresponding to a person and renders it in a stylized UI with a photo, job title, and other details.
51+
* A "video" component that acts as a wrapper around the HTML `<video>` element, making it more convenient to use in a Razor component.
52+
53+
* **Require interactive rendering** (Basic)
54+
55+
You can choose to require that your component is only used with interactive rendering. This limits the applicability of your component, but means that you may freely rely on arbitrary event handlers. Even then, you should still avoid declaring a specific `@rendermode` and permit the app author who consumes your library to select one.
56+
57+
Examples:
58+
59+
* A video editing component in which users may splice and re-order segments of video. Even if there was a way to represent these edit operations with plain HTML buttons and form posts, the user experience would be unacceptable without true interactivity.
60+
* A collaborative document editor that must show the activities of other users in real time.
61+
62+
* **Use interactive behaviors, but design for static SSR and progressive enhancement** (Advanced)
63+
64+
Many interactive behaviors can be implemented using only HTML capabilities. With a good understanding of HTML and CSS, you can often produce a useful baseline of functionality that works with static SSR. You can still declare event handlers that implement more advanced, optional behaviors, which only work in interactive render modes.
65+
66+
Examples:
67+
68+
* A grid component. Under static SSR, the component may only support displaying data and navigating between pages (implemented with `<a>` links). When used with interactive rendering, the component may add live sorting and filtering.
69+
* A tabset component. As long as navigation between tabs is achieved using `<a>` links and state is held only in URL query parameters, the component can work without `@onclick`.
70+
* An advanced file upload component. Under static SSR, the component may behave as a native `<input type=file>`. When used with interactive rendering, the component could also display upload progress.
71+
* A stock ticker. Under static SSR, the component may display the stock quote at the time the HTML was rendered. When used with interactive rendering, the component may then update the stock price in real time.
72+
73+
For any of these strategies, there's also the option of implementing interactive features with JavaScript.
74+
75+
To choose among these approaches, reusable Razor component authors must make a cost/benefit tradeoff. Your component is more useful and has a broader potential user base if it supports all render modes, including static SSR. However, it takes more work to design and implement a component that supports and takes best advantage of each render mode.
76+
77+
## When to use the `@rendermode` directive
78+
79+
In most cases, reusable component authors should **not** specify a render mode, even when interactivity is required. This is because the component author doesn't know whether the app enables support for `InteractiveServer`, `InteractiveWebAssembly`, or both with `InteractiveAuto`. By not specifying a `@rendermode`, the component author leaves the choice to the app developer.
80+
81+
Even if the component author thinks that interactivity is required, there may still be cases where an app author considers it sufficient to use static SSR alone. For example, a draggable, zoomable map component may seem to require interactivity. However, some scenarios may only call for rendering a static map image and avoiding drag/zoom features.
82+
83+
The only reason why a reusable component author should use the `@rendermode` directive on their component is if the implementation is fundamentally coupled to one specific render mode and would certainly cause an error if used in a different mode. Consider a component with a core purpose of interacting directly with the host OS using Windows or Linux-specific APIs. It might be impossible to use such a component on WebAssembly. If so, it's reasonable to declare `@rendermode InteractiveServer` for the component.
84+
85+
## Streaming rendering
86+
87+
Reusable Razor components are free to declare `@attribute [StreamRendering]` for [streaming rendering](xref:blazor/components/rendering#streaming-rendering). This results in incremental UI updates during static SSR. Since the same data-loading patterns produce incremental UI updates during interactive rendering (regardless of the [`StreamRendering` attribute](xref:Microsoft.AspNetCore.Components.StreamRenderingAttribute)), the component can behave correctly in all cases. Even in cases where streaming static SSR is suppressed on the server, the component still renders its correct final state.
88+
89+
## Using links across render modes
90+
91+
Reusable Razor components may use links and enhanced navigation. HTML `<a>` tags should produce equivalent behaviors with or without an interactive `<Router>` component and whether or not enhanced navigation is enabled/disabled at an ancestor level in the DOM.
92+
93+
## Using forms across render modes
94+
95+
Reusable Razor components may include forms (either `<form @onsubmit=...>` or `<EditForm OnValidSubmit=...>`), as these can be implemented to work equivalently across both static and interactive render modes.
96+
97+
Consider the following example:
98+
99+
```razor
100+
<EditForm Enhance FormName="NewProduct" Model="@Model" OnValidSubmit="SaveProduct">
101+
<DataAnnotationsValidator />
102+
<ValidationSummary />
103+
104+
<p>Name: <InputText @bind-Value="@Item.Name" /></p>
105+
106+
<button type="submit">Submit</button>
107+
</EditForm>
108+
109+
@code {
110+
[SupplyParameterFromForm]
111+
public Product? Model { get; set; }
112+
113+
protected override void OnInitialized() => Model ??= new();
114+
115+
private async Task Save()
116+
{
117+
...
118+
}
119+
}
120+
```
121+
122+
The `Enhance`, `FormName`, and `SupplyParameterFromForm` APIs are only used during static SSR and are ignored during interactive rendering. The form works correctly during both interactive and static SSR.
123+
124+
## Avoid APIs that are specific to static SSR
125+
126+
To make a reusable component that works across all render modes, don't rely on <xref:Microsoft.AspNetCore.Http.HttpContext> because it's only available during static SSR. The <xref:Microsoft.AspNetCore.Http.HttpContext> API doesn't make sense to use during interactive rendering because there's no active HTTP request in flight at those times. It's meaningless to think about setting a status code or writing to the response.
127+
128+
Reusable components are free to receive an <xref:Microsoft.AspNetCore.Http.HttpContext> when available, as follows:
129+
130+
```csharp
131+
[CascadingParameter]
132+
public HttpContext? Context { get; set; }
133+
```
134+
135+
The value is `null` during interactive rendering and is only set during static SSR.
136+
137+
In many cases, there are better alternatives than using <xref:Microsoft.AspNetCore.Http.HttpContext>. If you need to know the current URL or to perform a redirection, the APIs on <xref:Microsoft.AspNetCore.Components.NavigationManager> work with all render modes. If you need to know the user's authentication state, use Blazor's <xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider> service over using <xref:Microsoft.AspNetCore.Http.HttpContext.User?displayProperty=nameWithType>.

aspnetcore/blazor/components/render-modes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ The following table shows the available render modes for rendering Razor compone
2929

3030
Name | Description | Render location | Interactive
3131
---- | ----------- | :-------------: | :---------:
32-
Static | Static server rendering | Server | <span aria-hidden="true">❌</span><span class="visually-hidden">No</span>
32+
Static Server | Static server rendering | Server | <span aria-hidden="true">❌</span><span class="visually-hidden">No</span>
3333
Interactive Server | Interactive server rendering using Blazor Server | Server | <span aria-hidden="true">✔️</span><span class="visually-hidden">Yes</span>
3434
Interactive WebAssembly | Interactive client rendering using Blazor WebAssembly | Client | <span aria-hidden="true">✔️</span><span class="visually-hidden">Yes</span>
3535
Interactive Auto | Interactive client rendering using Blazor Server initially and then WebAssembly on subsequent visits after the Blazor bundle is downloaded | Server, then client | <span aria-hidden="true">✔️</span><span class="visually-hidden">Yes</span>
3636

37-
Prerendering is enabled by default for interactive components. Guidance on controlling prerendering is provided later in this article.
37+
Prerendering is enabled by default for interactive components. Guidance on controlling prerendering is provided later in this article. For general industry terminology on client and server rendering concepts, see <xref:blazor/fundamentals/index#client-and-server-rendering-concepts>.
3838

3939
The following examples demonstrate setting the component's render mode with a few basic Razor component features.
4040

@@ -589,4 +589,4 @@ At the moment, the shorthand render mode approach is probably only useful for re
589589

590590
* <xref:blazor/js-interop/ssr>
591591
* [Cascading values/parameters and render mode boundaries](xref:blazor/components/cascading-values-and-parameters#cascading-valuesparameters-and-render-mode-boundaries): Also see the [Root-level cascading parameters](xref:blazor/components/cascading-values-and-parameters#root-level-cascading-parameters) section earlier in the article.
592-
592+
* <xref:blazor/components/class-libraries-with-static-ssr>

aspnetcore/blazor/fundamentals/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ Throughout the Blazor documentation, activity that takes place on the user's sys
2020

2121
The term *rendering* means to produce the HTML markup that browsers display.
2222

23-
*Client-side rendering* means that the final HTML markup is generated by the Blazor WebAssembly runtime on the client. No HTML for the app's client-generated UI is sent from a server to the client for this type of rendering.
23+
*Client-side rendering (CSR)* means that the final HTML markup is generated by the Blazor WebAssembly runtime on the client. No HTML for the app's client-generated UI is sent from a server to the client for this type of rendering. User interactivity with the page is assumed. There's no such concept as *static* client-side rendering.
2424

25-
*Server-side rendering* means that the final HTML markup is generated by the ASP.NET Core runtime on the server. The HTML is sent to the client over a network for display by the client's browser. No HTML for the app's server-generated UI is created by the client for this type of rendering.
25+
*Server-side rendering (SSR)* means that the final HTML markup is generated by the ASP.NET Core runtime on the server. The HTML is sent to the client over a network for display by the client's browser. No HTML for the app's server-generated UI is created by the client for this type of rendering. SSR can be of two varieties: *static SSR*, where the server produces static HTML that doesn't provide for user interactivity or component state with Blazor, and *interactive SSR*, where Blazor events permit user interactivity and component state is maintained by the Blazor framework.
2626

2727
*Prerendering* is the process of initially rendering page content on the server without enabling event handlers for rendered controls. The server outputs the HTML UI of the page as soon as possible in response to the initial request, which makes the app feel more responsive to users. Prerendering can also improve [Search Engine Optimization (SEO)](https://developer.mozilla.org/docs/Glossary/SEO) by rendering content for the initial HTTP response that search engines use to calculate page rank. Prerendering is always followed by final rendering, either on the server or the client.
2828

aspnetcore/migration/70-80.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ The following migration scenarios are covered:
8888
* [Migrate the `BlazorEnableCompression` MSBuild property](#migrate-the-blazorenablecompression-msbuild-property)
8989
* [Migrate the `<CascadingAuthenticationState>` component to cascading authentication state services](#migrate-the-cascadingauthenticationstate-component-to-cascading-authentication-state-services)
9090
* [*New article*: HTTP caching issues during migration](#new-article-on-http-caching-issues)
91+
* [*New article*: New article on class libraries with static server-side rendering (static SSR)](#new-article-on-class-libraries-with-static-server-side-rendering-static-ssr)
9192

9293
For guidance on adding Blazor support to an ASP.NET Core app, see <xref:blazor/components/integration#add-blazor-support-to-an-aspnet-core-app>.
9394

@@ -191,7 +192,7 @@ Blazor Server apps are supported in .NET 8 without any code changes. Use the fol
191192
```
192193

193194
> [!NOTE]
194-
> The preceding configuration assumes that the app's components adopt interactive server rendering. For more information, including how to adopt static server rendering, see <xref:blazor/components/render-modes>.
195+
> The preceding configuration assumes that the app's components adopt interactive server rendering. For more information, including how to adopt static server-side rendering (SSR), see <xref:blazor/components/render-modes>.
195196
196197
Remove the Environment Tag Helpers for error UI and replace them with the following Razor markup.
197198

@@ -517,6 +518,12 @@ We've added a new article that discusses some of the common HTTP caching issues
517518

518519
For more information, see <xref:blazor/http-caching-issues>.
519520

521+
### New article on class libraries with static server-side rendering (static SSR)
522+
523+
We've added a new article that discusses component library authorship in Razor class libraries (RCLs) with static server-side rendering (static SSR).
524+
525+
For more information, see <xref:blazor/components/class-libraries-with-static-ssr>.
526+
520527
## Docker
521528

522529
### Update Docker images

0 commit comments

Comments
 (0)