Reducing Perceived Latency in Blazor SSR with Streaming Rendering
Learn how to use [StreamRendering] in .NET 8 Blazor to eliminate blank screens and improve perceived performance in Static SSR pages.
18 Apr 2026, 04:10 UTC

The 'Blank Page' Problem in Static SSR
When building a Blazor Web App using Static Server-Side Rendering (SSR), the server typically waits for all asynchronous tasks—such as database queries or API calls—to complete before sending the final HTML to the browser. If a page depends on a slow legacy API or a complex EF Core query, the user sees a blank screen or a browser loading spinner until the slowest task finishes. This creates a poor user experience, even if the rest of the page layout is ready to be displayed.
Streaming rendering solves this by allowing the server to send the initial page layout immediately, using placeholder content for the slow parts, and then "streaming" the actual data into the page as the tasks complete over the same HTTP connection.
How Streaming Rendering Works
Introduced in .NET 8, streaming rendering allows a component to render twice. First, it renders synchronously with whatever data is currently available (usually nulls or defaults). The server flushes this initial HTML to the client immediately. As the awaited tasks in the component's lifecycle finish, the server re-renders the component and sends the updated HTML chunks to the browser.
Crucially, this does not require a full Blazor Server (SignalR) circuit or WebAssembly runtime to be active. A small, built-in JavaScript snippet handles the DOM patching, meaning the page feels responsive before any heavy interactivity is initialized.
Implementing a Streaming Product List
To enable this behavior, you apply the [StreamRendering] attribute to the page component. Below is a comparison of a blocking implementation versus a streaming implementation.
The Blocking Approach (Default):
@page "/products"
@inject IProductService ProductService
<h1>Product Catalog</h1>
@if (products == null)
{
<p>Loading...</p>
}
else
{
<ul>
@foreach (var p in products) { <li>@p.Name</li> }
</ul>
}
@code {
private List<Product>? products;
protected override async Task OnInitializedAsync()
{
// The server waits here; the user sees nothing until this finishes
products = await ProductService.GetProductsAsync();
}
}
The Streaming Approach:
@page "/products"
@using Microsoft.AspNetCore.Components
@inject IProductService ProductService
[StreamRendering]
<h1>Product Catalog</h1>
@if (products == null)
{
<div class="skeleton-loader">Fetching products from database...</div>
}
else
{
<ul>
@foreach (var p in products) { <li>@p.Name</li> }
</ul>
}
@code {
private List<Product>? products;
protected override async Task OnInitializedAsync()
{
// The server flushes the 'Fetching...' message immediately
products = await ProductService.GetProductsAsync();
// Once this completes, the server streams the updated list
}
}
Trade-offs and Resource Management
While streaming rendering improves the perceived load time, it is not a free optimization. There are specific engineering trade-offs to consider:
- Server Connection Duration: In a standard SSR request, the HTTP response is closed as soon as the HTML is sent. With streaming, the connection remains open until all awaited tasks in the component are complete. On high-traffic sites, this increases the number of concurrent open connections on the server.
- SEO Considerations: While most modern search engines handle JavaScript, some basic crawlers or social media link unfurlers may only see the initial "Loading..." state and miss the streamed content.
- Interactivity Conflict: Streaming rendering is a feature of Static SSR. Once you switch a component's render mode to
InteractiveServerorInteractiveWebAssembly, the standard Blazor component lifecycle takes over, and[StreamRendering]no longer applies.
Verifying the Stream
To verify that streaming is actually occurring rather than a standard render, follow these steps:
- Open your browser's Developer Tools and go to the Network tab.
- Load the page with the
[StreamRendering]attribute. - Observe the Timing or Response tab. You should see the initial HTML arrive almost instantly, followed by a pause, and then the remaining HTML chunks appearing in the response body without a new request being made.
Actionable Summary
Use [StreamRendering] for read-heavy pages that depend on one or two slow data sources. It is an ideal middle ground between a completely static page and a fully interactive Blazor component, providing the speed of SSR with the perceived responsiveness of a modern SPA.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.