Skip to content

Commit 797a9fe

Browse files
committed
Statically-rendered layout components
1 parent a29ecfe commit 797a9fe

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

aspnetcore/blazor/components/built-in-components.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
3737
* [`InputSelect`](xref:blazor/forms/input-components)
3838
* [`InputText`](xref:blazor/forms/input-components)
3939
* [`InputTextArea`](xref:blazor/forms/input-components)
40-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
40+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
4141
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
4242
* [`NavigationLock`](xref:blazor/fundamentals/routing#handleprevent-location-changes)
4343
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
@@ -77,7 +77,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
7777
* [`InputSelect`](xref:blazor/forms/input-components)
7878
* [`InputText`](xref:blazor/forms/input-components)
7979
* [`InputTextArea`](xref:blazor/forms/input-components)
80-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
80+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
8181
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
8282
* [`NavigationLock`](xref:blazor/fundamentals/routing#handleprevent-location-changes)
8383
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
@@ -115,7 +115,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
115115
* [`InputSelect`](xref:blazor/forms/input-components)
116116
* [`InputText`](xref:blazor/forms/input-components)
117117
* [`InputTextArea`](xref:blazor/forms/input-components)
118-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
118+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
119119
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
120120
* [`NavigationLock`](xref:blazor/fundamentals/routing#handleprevent-location-changes)
121121
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
@@ -149,7 +149,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
149149
* [`InputSelect`](xref:blazor/forms/input-components)
150150
* [`InputText`](xref:blazor/forms/input-components)
151151
* [`InputTextArea`](xref:blazor/forms/input-components)
152-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
152+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
153153
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
154154
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
155155
* [`OwningComponentBase`](xref:fundamentals/dependency-injection#utility-base-component-classes-to-manage-a-di-scope)
@@ -177,7 +177,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
177177
* [`InputSelect`](xref:blazor/forms/input-components)
178178
* [`InputText`](xref:blazor/forms/input-components)
179179
* [`InputTextArea`](xref:blazor/forms/input-components)
180-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
180+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
181181
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
182182
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
183183
* [`OwningComponentBase`](xref:fundamentals/dependency-injection#utility-base-component-classes-to-manage-a-di-scope)
@@ -203,7 +203,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
203203
* [`InputSelect`](xref:blazor/forms/input-components)
204204
* [`InputText`](xref:blazor/forms/input-components)
205205
* [`InputTextArea`](xref:blazor/forms/input-components)
206-
* [`LayoutComponentBase`](xref:blazor/components/layouts#layout-components)
206+
* [`LayoutComponentBase`](xref:blazor/components/layouts#create-a-layout-component)
207207
* [`LayoutView`](xref:blazor/components/layouts#apply-a-layout-to-arbitrary-content-layoutview-component)
208208
* [`NavLink`](xref:blazor/fundamentals/routing#navlink-component)
209209
* [`OwningComponentBase`](xref:fundamentals/dependency-injection#utility-base-component-classes-to-manage-a-di-scope)

aspnetcore/blazor/components/layouts.md

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ Some app elements, such as menus, copyright messages, and company logos, are usu
2020

2121
A Blazor layout is a Razor component that shares markup with components that reference it. Layouts can use [data binding](xref:blazor/components/data-binding), [dependency injection](xref:blazor/fundamentals/dependency-injection), and other features of components.
2222

23-
## Layout components
24-
25-
### Create a layout component
23+
## Create a layout component
2624

2725
To create a layout component:
2826

@@ -73,7 +71,7 @@ The following `DoctorWhoLayout` component shows the Razor template of a layout c
7371

7472
:::moniker-end
7573

76-
### `MainLayout` component
74+
## `MainLayout` component
7775

7876
In an app created from a [Blazor project template](xref:blazor/project-structure), the `MainLayout` component is the app's [default layout](#apply-a-default-layout-to-an-app). Blazor's layout adopts the [:::no-loc text="Flexbox"::: layout model](https://developer.mozilla.org/docs/Glossary/Flexbox) ([W3C specification](https://www.w3.org/TR/css-flexbox-1/)).
7977

@@ -94,6 +92,76 @@ In an app created from a [Blazor project template](xref:blazor/project-structure
9492

9593
:::moniker-end
9694

95+
:::moniker range=">= aspnetcore-8.0"
96+
97+
<!-- UPDATE 11.0 Is https://github.com/dotnet/aspnetcore/issues/52768 addressed
98+
to resolve the following limitation? -->
99+
100+
## Statically-rendered layout components
101+
102+
When a Blazor Web App adopts per-page/component rendering (the `Routes` component doesn't specify an interactive render mode), layout components are rendered statically on the server. Applying an interactive render mode directly to a layout isn't supported because Blazor doesn't support serializing a <xref:Microsoft.AspNetCore.Components.RenderFragment> (`@Body` in this case) as a root component parameter. For example, placing `@rendermode InteractiveServer` at the top of the `MainLayout` component results in the following runtime exception:
103+
104+
> :::no-loc text="System.InvalidOperationException: Cannot pass the parameter 'Body' to component 'MainLayout' with rendermode 'InteractiveServerRenderMode'. This is because the parameter is of the delegate type 'Microsoft.AspNetCore.Components.RenderFragment', which is arbitrary code and cannot be serialized.":::
105+
106+
This applies to any layout component that inherits from <xref:Microsoft.AspNetCore.Components.LayoutComponentBase> in an app that adopts per-page/component rendering.
107+
108+
This scenario might be addressed in a future release of Blazor. For more information, see [[Blazor] Support serializing render fragments from SSR (`dotnet/aspnetcore` #52768)](https://github.com/dotnet/aspnetcore/issues/52768). In the meantime, you can adopt the following approach in a Blazor Web App that adopts per-page/component rendering.
109+
110+
Create a wrapper component that's capable of interactivity. In the following example, a wrapper component contains a [Blazor section](xref:blazor/components/sections) that can receive content from a child component.
111+
112+
`Pages/InteractiveWrapper.razor`:
113+
114+
```razor
115+
@rendermode InteractiveServer
116+
117+
<div>
118+
<SectionOutlet SectionName="top-bar" />
119+
</div>
120+
121+
@ChildContent
122+
123+
@code {
124+
[Parameter]
125+
public RenderFragment? ChildContent { get; set; }
126+
}
127+
```
128+
129+
The `Counter` component can use the wrapper component and set interactive section content. In the following example, an interactively-rendered counter button is placed in the section.
130+
131+
`Pages/Counter.razor`:
132+
133+
```razor
134+
@page "/counter"
135+
@rendermode InteractiveServer
136+
137+
<InteractiveWrapper>
138+
139+
<SectionContent SectionName="top-bar">
140+
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
141+
</SectionContent>
142+
143+
<PageTitle>Counter</PageTitle>
144+
145+
<h1>Counter</h1>
146+
147+
<p role="status">Current count: @currentCount</p>
148+
149+
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
150+
151+
</InteractiveWrapper>
152+
153+
@code {
154+
private int currentCount = 0;
155+
156+
private void IncrementCount()
157+
{
158+
currentCount++;
159+
}
160+
}
161+
```
162+
163+
:::moniker-end
164+
97165
## Apply a layout
98166

99167
### Make the layout namespace available

aspnetcore/blazor/components/render-modes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,11 @@ Non-serializable component parameters, such as child content or a render fragmen
576576

577577
> :::no-loc text="System.InvalidOperationException: Cannot pass the parameter 'ChildContent' to component 'SharedMessage' with rendermode 'InteractiveServerRenderMode'. This is because the parameter is of the delegate type 'Microsoft.AspNetCore.Components.RenderFragment', which is arbitrary code and cannot be serialized.":::
578578
579+
<!-- UPDATE 11.0 Is https://github.com/dotnet/aspnetcore/issues/52768 addressed
580+
to resolve the following limitation? -->
581+
582+
The same thing happens if you attempt to adopt interactive rendering in a layout that inherits from <xref:Microsoft.AspNetCore.Components.LayoutComponentBase>, such as the app's `MainLayout` component, in an app that adopts per-page/component rendering. For more information, see <xref:blazor/components/layouts#statically-rendered-layout-components>.
583+
579584
To circumvent the preceding limitation, wrap the child component in another component that doesn't have the parameter. This is the approach taken in the Blazor Web App project template with the `Routes` component (`Components/Routes.razor`) to wrap the <xref:Microsoft.AspNetCore.Components.Routing.Router> component.
580585

581586
`WrapperComponent.razor`:

0 commit comments

Comments
 (0)