.NET 8 introduces Sections, a powerful feature that allows components to render content into specific areas defined in a layout. If you're familiar with ASP.NET WebForms or ASP.NET MVC, this concept is similar to ContentPlaceHolder or @RenderSection.
Sections enable pages to inject their own content (like toolbars, sidebars, or footers) into predefined areas of a layout without modifying the layout itself.
Key Components
| ComponentDescription |
SectionOutlet | Defines where content will be rendered (in layout) |
SectionContent | Provides what content to render (in pages/components) |
How Sections Work
┌─────────────────────────────────────────────────────────────┐
│ MainLayout.razor │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ <SectionOutlet SectionName="PageActions" /> │ │
│ │ ▲ │ │
│ │ │ Content rendered here │ │
│ └─────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ @Body (Page Content) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
▲
│ Provides content via
│
┌─────────────────────────────────────────────────────────────┐
│ MyPage.razor │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ <SectionContent SectionName="PageActions"> │ │
│ │ <button>Add</button> │ │
│ │ <button>Save</button> │ │
│ │ </SectionContent> │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Basic Usage
Step 1: Define SectionOutlet in Layout
MainLayout.razor:
@inherits LayoutComponentBase
<div class="page">
<main>
<div class="top-row px-4">
@* Section for page-specific actions *@
<SectionOutlet SectionName="PageActions" />
</div>
<article class="content px-4">
@Body
</article>
<footer class="px-4 py-3">
@* Section for page-specific footer *@
<SectionOutlet SectionName="PageFooter" />
</footer>
</main>
</div>
Step 2: Provide SectionContent from Page
ProductsPage.razor:
@page "/products"
@rendermode InteractiveServer
<SectionContent SectionName="PageActions">
<button class="btn btn-primary" @onclick="AddProduct">Add Product</button>
<button class="btn btn-success" @onclick="ExportProducts">Export</button>
</SectionContent>
<SectionContent SectionName="PageFooter">
<span>Total Products: @products.Count</span>
</SectionContent>
<h1>Products</h1>
@* Page content here *@
@code {
private List<Product> products = [];
private void AddProduct() { /* ... */ }
private void ExportProducts() { /* ... */ }
}
Render Mode Requirement
Sections require an interactive render mode to function properly:
@page "/my-page"
@rendermode InteractiveServer
| Render ModeWorks with Sections |
| Static SSR | ❌ No |
InteractiveServer | ✅ Yes |
InteractiveWebAssembly | ✅ Yes |
InteractiveAuto | ✅ Yes |
Common Use Cases
1. Page-Specific Toolbar
@* In Layout *@
<header class="toolbar">
<SectionOutlet SectionName="Toolbar" />
</header>
@* In Page *@
<SectionContent SectionName="Toolbar">
<button @onclick="Save">Save</button>
<button @onclick="Cancel">Cancel</button>
</SectionContent>
2. Sidebar Content
@* In Layout *@
<aside class="sidebar">
<SectionOutlet SectionName="Sidebar" />
</aside>
@* In Page *@
<SectionContent SectionName="Sidebar">
<nav>
<a href="#section1">Section 1</a>
<a href="#section2">Section 2</a>
</nav>
</SectionContent>
3. Modal Dialogs
@* In Layout (at root level for proper z-index) *@
<SectionOutlet SectionName="Modals" />
@* In Page *@
<SectionContent SectionName="Modals">
@if (showConfirmDialog)
{
<div class="modal">
<p>Are you sure?</p>
<button @onclick="Confirm">Yes</button>
<button @onclick="Cancel">No</button>
</div>
}
</SectionContent>
4. Breadcrumbs
@* In Layout *@
<nav class="breadcrumb-container">
<SectionOutlet SectionName="Breadcrumbs" />
</nav>
@* In Page *@
<SectionContent SectionName="Breadcrumbs">
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li class="active">Edit Product</li>
</ol>
</SectionContent>
Complete Example
MainLayout.razor
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4 d-flex justify-content-between">
<div>
<SectionOutlet SectionName="PageActions" />
</div>
<div>
<a href="/about">About</a>
</div>
</div>
<article class="content px-4">
@Body
</article>
<footer class="px-4 py-3 border-top">
<SectionOutlet SectionName="PageFooter" />
</footer>
</main>
</div>
<SectionOutlet SectionName="Modals" />
OrdersPage.razor
@page "/orders"
@rendermode InteractiveServer
<PageTitle>Orders</PageTitle>
<SectionContent SectionName="PageActions">
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" @onclick="CreateOrder">
New Order
</button>
<button class="btn btn-outline-success" @onclick="ExportOrders">
Export
</button>
<button class="btn btn-outline-secondary" @onclick="RefreshOrders">
Refresh
</button>
</div>
</SectionContent>
<SectionContent SectionName="PageFooter">
<div class="d-flex justify-content-between text-muted small">
<span>Total Orders: @orders.Count</span>
<span>Last Refresh: @lastRefresh.ToString("HH:mm:ss")</span>
</div>
</SectionContent>
<SectionContent SectionName="Modals">
@if (showNewOrderModal)
{
<div class="modal fade show d-block" style="background: rgba(0,0,0,0.5);">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5>New Order</h5>
<button class="btn-close" @onclick="CloseModal"></button>
</div>
<div class="modal-body">
<input @bind="newOrderName" class="form-control" placeholder="Order name" />
</div>
<div class="modal-footer">
<button class="btn btn-primary" @onclick="SaveOrder">Save</button>
<button class="btn btn-secondary" @onclick="CloseModal">Cancel</button>
</div>
</div>
</div>
</div>
}
</SectionContent>
<h1>Orders</h1>
<table class="table">
<thead>
<tr>
<th>Order ID</th>
<th>Name</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@foreach (var order in orders)
{
<tr>
<td>@order.Id</td>
<td>@order.Name</td>
<td>@order.Date.ToShortDateString()</td>
</tr>
}
</tbody>
</table>
@code {
private List<Order> orders = [];
private DateTime lastRefresh = DateTime.Now;
private bool showNewOrderModal = false;
private string newOrderName = "";
protected override void OnInitialized()
{
orders = [
new(1, "Order A", DateTime.Now.AddDays(-5)),
new(2, "Order B", DateTime.Now.AddDays(-3)),
new(3, "Order C", DateTime.Now.AddDays(-1))
];
}
private void CreateOrder() => showNewOrderModal = true;
private void CloseModal() => showNewOrderModal = false;
private void SaveOrder()
{
orders.Add(new(orders.Count + 1, newOrderName, DateTime.Now));
newOrderName = "";
showNewOrderModal = false;
}
private void ExportOrders() { /* Export logic */ }
private void RefreshOrders() => lastRefresh = DateTime.Now;
private record Order(int Id, string Name, DateTime Date);
}
Multiple SectionContents
If multiple components provide content for the same section, the last rendered one wins:
@* Component A (rendered first) *@
<SectionContent SectionName="Title">
<h1>Title from A</h1>
</SectionContent>
@* Component B (rendered second) *@
<SectionContent SectionName="Title">
<h1>Title from B</h1> @* This one is displayed *@
</SectionContent>
Empty Sections
If no SectionContent is provided for a SectionOutlet, the outlet simply renders nothing:
@* Layout *@
<SectionOutlet SectionName="OptionalContent" />
@* Page that doesn't define SectionContent *@
@* Nothing appears in the outlet - this is OK *@
Sections vs. RenderFragment Parameters
| FeatureSectionsRenderFragment |
| Scope | Layout-wide | Parent-child only |
| Definition location | Layout | Component parameter |
| Multiple sources | Yes (last wins) | No |
| Use case | Page→Layout content injection | Component composition |
Use Sections when: Pages need to inject content into layout areas.
Use RenderFragment when: A component accepts child content from its parent.
Common Issues
| IssueCauseSolution |
| Content doesn't appear | Missing render mode | Add @rendermode InteractiveServer |
| Wrong section name | Typo in name | Verify SectionName matches exactly |
| Content from wrong page | Navigation issue | Sections auto-update on navigation |
Summary
| ComponentLocationPurpose |
SectionOutlet | Layout | Defines where content appears |
SectionContent | Page/Component | Provides the content |
@* Layout: Define the outlet *@
<SectionOutlet SectionName="MySection" />
@* Page: Provide the content *@
<SectionContent SectionName="MySection">
<p>My content here</p>
</SectionContent>