# .NET 9 Blazor: Pełnostackowy rozwój aplikacji z Blazor United w 2026
> .NET 9 Blazor United łączy statyczny SSR, Server i WebAssembly w jednym frameworku full-stack. Praktyczny poradnik obejmujący tryby renderowania, streaming rendering, wstrzykiwanie zależności i wzorce produkcyjne.
- Published: 2026-04-26
- Updated: 2026-04-26
- Author: SharpSkill
- Tags: dotnet, blazor, aspnet-core, webassembly, full-stack
- Reading time: 8 min
---
.NET 9 wprowadza Blazor United — architekturę, która scala trzy dotychczas rozdzielne strategie renderowania w jeden spójny model programowania. Deweloperzy zyskują możliwość łączenia statycznego Server-Side Rendering, interaktywności przez SignalR oraz WebAssembly w obrębie tego samego komponentu, a nawet tej samej strony. Eliminuje to kompromisy między wydajnością pierwszego załadowania, SEO a możliwościami aplikacji działających całkowicie po stronie klienta. Blazor United to odpowiedź na rosnące wymagania współczesnych aplikacji webowych, które muszą być jednocześnie szybkie, interaktywne i łatwe w utrzymaniu.
> **Czym jest Blazor United?**
>
> Blazor United to zunifikowany model renderowania w .NET 9, który pozwala mieszać tryby statyczne, Server (SignalR) i WebAssembly w jednej aplikacji bez konieczności tworzenia oddzielnych projektów. Komponent może renderować się statycznie dla SEO, a po załadowaniu przełączyć się na interaktywność przez WebAssembly — wszystko automatycznie.
## Konfiguracja projektu .NET 9 Blazor Web App
Rozpoczęcie pracy z Blazor United wymaga .NET 9 SDK. Nowy szablon `blazorweb` umożliwia wybór trybu interaktywności już podczas tworzenia projektu. Opcja `--interactivity Auto` włącza hybrydowe renderowanie — serwer obsługuje pierwszą interakcję, a następnie aplikacja przełącza się na WebAssembly po pobraniu runtime'u w tle.
```bash
# Create a new Blazor Web App with all render modes enabled
dotnet new blazorweb -n FullStackApp --interactivity Auto --all-interactive false
cd FullStackApp
dotnet run
```
Parametr `--all-interactive false` wymusza selektywną interaktywność — domyślnie komponenty renderują się statycznie, chyba że explicite zaznaczono inaczej przez atrybut `@rendermode`. To podejście optymalizuje transfer danych i eliminuje niepotrzebne połączenia SignalR dla treści, która nie wymaga dynamiki.
## Tryby renderowania i ich zastosowania
Blazor United opiera się na czterech trybach renderowania, które można przypisywać zarówno całym stronom, jak i pojedynczym komponentom. Każdy tryb ma specyficzne zastosowania zależne od wymagań dotyczących wydajności, SEO i interaktywności.
| Render Mode | Hosting | Interactivity | Best For |
|---|---|---|---|
| Static SSR | Server | None | Marketing pages, docs, SEO content |
| Interactive Server | Server via SignalR | Full | Dashboards, CRUD, forms |
| Interactive WebAssembly | Browser via WASM | Full | Offline, heavy client-side logic |
| Interactive Auto | Server then WASM | Full | Best of both — fast load + independence |
Statyczny SSR generuje czysty HTML bez JavaScript — idealny dla landing pages, dokumentacji i wszystkiego, co musi być indeksowane przez wyszukiwarki. Interactive Server utrzymuje połączenie WebSocket i wykonuje kod C# na serwerze, co eliminuje opóźnienia sieciowe dla aplikacji wymagających częstych aktualizacji UI. WebAssembly przenosi całą logikę do przeglądarki, umożliwiając offline-first experiences i złożone operacje bez obciążania serwera. Auto mode łączy oba — pierwsze kliknięcie obsługuje serwer, a w międzyczasie pobiera runtime WASM dla przyszłych interakcji.
```csharp
// Components/Pages/Dashboard.razor
@page "/dashboard"
@rendermode InteractiveServer
Dashboard
Real-Time Metrics
```
W powyższym przykładzie cała strona `/dashboard` renderuje się przez SignalR. Oznacza to, że każde zdarzenie (kliknięcie, input) wysyła wiadomość do serwera, który przetwarza logikę i zwraca zaktualizowany fragment DOM. Ten tryb sprawdza się w dashboardach czasu rzeczywistego, gdzie dane zmieniają się co kilka sekund i wymagają synchronizacji z bazą danych.
Adaptacyjne komponenty mogą wykrywać kontekst renderowania i dostosowywać zachowanie:
```csharp
// Components/Shared/AdaptiveComponent.razor
@if (RendererInfo.IsInteractive)
{
}
else
{
Static Fallback Link
}
@code {
private void HandleClick()
{
// Runs only in interactive modes (Server or WASM)
}
}
```
`RendererInfo.IsInteractive` zwraca `true` tylko w trybach Server, WebAssembly i Auto. Dzięki temu ten sam komponent może być używany zarówno w statycznych dokumentach, jak i interaktywnych modułach aplikacji — w pierwszym przypadku wyświetli link, w drugim przycisk z logiką C#.
## Streaming rendering dla wydajności postrzeganej
Atrybut `[StreamRendering]` włącza progresywne renderowanie HTML. Zamiast czekać na zakończenie wszystkich asynchronicznych operacji, Blazor wysyła fragmenty strony w miarę ich gotowości. Użytkownik widzi szkielet UI niemal natychmiast, a treść pojawia się stopniowo w tle.
```csharp
// Components/Pages/Products.razor
@page "/products"
@attribute [StreamRendering]
Product Catalog
@if (products is null)
{
Loading products...
}
else
{
@foreach (var product in products)
{
}
}
@code {
private List? products;
// Data loads asynchronously; streaming pushes updates to the browser
protected override async Task OnInitializedAsync()
{
products = await ProductService.GetAllAsync();
}
}
```
Podczas pierwszego renderu przeglądarka otrzymuje HTML z komunikatem "Loading products...". Gdy `ProductService.GetAllAsync()` zakończy pobieranie danych, Blazor wysyła aktualizację przez ten sam strumień HTTP, zastępując placeholder listą produktów. Z perspektywy użytkownika strona ładuje się dwuetapowo — najpierw nagłówek i layout, potem zawartość. Redukuje to postrzegane opóźnienie, szczególnie przy wolnych zapytaniach do bazy danych.
Streaming rendering działa tylko w trybie statycznego SSR. Jeśli komponent ma `@rendermode InteractiveServer`, atrybut `[StreamRendering]` zostanie zignorowany — SignalR zarządza aktualizacjami przez własny protokół.
## Wstrzykiwanie zależności przez konstruktory
.NET 9 wprowadza primary constructors dla komponentów Razor. Zamiast deklarować właściwości z `[Inject]`, można przekazać zależności bezpośrednio w konstruktorze, co poprawia czytelność i eliminuje boilerplate.
```csharp
// Components/Pages/OrderHistory.razor
@page "/orders"
@rendermode InteractiveServer
Order History
@if (orders is not null)
{
Order ID
Date
Total
@foreach (var order in orders)
{
@order.Id
@order.PlacedAt.ToShortDateString()
@order.Total.ToString("C")
}
}
@code {
// Constructor injection via primary constructor
[Inject] public required IOrderService OrderService { get; set; }
[Inject] public required NavigationManager Nav { get; set; }
private List? orders;
protected override async Task OnInitializedAsync()
{
orders = await OrderService.GetRecentOrdersAsync();
}
}
```
W tym przykładzie `IOrderService` i `NavigationManager` są wstrzykiwane przez kontenera DI w momencie tworzenia instancji komponentu. Modifier `required` wymusza inicjalizację przed pierwszym użyciem, co chroni przed błędami `NullReferenceException` w runtime. Taka składnia jest spójna z resztą ekosystemu C# 12 i ułatwia testowanie — wystarczy stworzyć mock serwisu i przekazać go do komponentu w unit teście.
## Interactive Auto — najlepsze z dwóch światów
Tryb Auto łączy Server i WebAssembly w jednym komponencie. Pierwsza interakcja (kliknięcie, input) jest obsługiwana przez SignalR, jednocześnie w tle pobiera się runtime .NET WASM. Po zakończeniu pobierania kolejne interakcje wykonują się lokalnie w przeglądarce bez opóźnień sieciowych.
```csharp
// Components/Pages/Chat.razor
@page "/chat"
@rendermode InteractiveAuto
Live Chat
@foreach (var msg in messages)
{
@msg.Author: @msg.Text
}
@code {
[Inject] public required IChatService ChatService { get; set; }
private List messages = new();
private string newMessage = string.Empty;
protected override async Task OnInitializedAsync()
{
messages = await ChatService.GetRecentMessagesAsync();
}
private async Task HandleKey(KeyboardEventArgs e)
{
if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(newMessage))
{
await ChatService.SendAsync(newMessage);
newMessage = string.Empty;
}
}
}
```
Podczas pierwszego wejścia na `/chat` przeglądarka otrzymuje HTML z listą wiadomości. Jeśli użytkownik od razu zacznie pisać, zdarzenie `@onkeydown` trafia do serwera przez SignalR — odpowiedź przychodzi w ciągu 20–50 ms zależnie od latencji. Równolegle w tle ładuje się 1.5–2 MB runtime .NET (skompresowany gzip). Po zakończeniu pobierania Blazor automatycznie przełącza renderowanie na WebAssembly — kolejne naciśnięcia klawiszy wykonują się lokalnie bez żadnego ruchu sieciowego. Użytkownik nie zauważa momentu przełączenia, a UX pozostaje płynny.
Interactive Auto eliminuje kompromis między szybkością pierwszego załadowania a responsywnością aplikacji. Sprawdza się w formularzach z walidacją po stronie klienta, edytorach WYSIWYG i wszystkich interfejsach wymagających natychmiastowej reakcji na input użytkownika. Jest to domyślna rekomendacja dla większości nowych projektów Blazor w 2026 roku, co często pojawia się w blazor interview questions 2026 jako best practice.
## Resilient connections — obsługa utraconych połączeń
Tryb Interactive Server opiera się na WebSocket. Utrata połączenia (zmiana sieci, uśpienie laptopa, restart serwera) przerywa działanie aplikacji. .NET 9 wprowadza konfigurowalny mechanizm ponownego łączenia z wykładniczym backoff.
```javascript
// wwwroot/app.js — Custom reconnection configuration
Blazor.start({
circuit: {
reconnectionOptions: {
retryIntervalMilliseconds: (retryNumber) => {
// Exponential backoff: 200ms, 400ms, 800ms, max 30s
return Math.min(200 * Math.pow(2, retryNumber), 30000);
},
maxRetries: 15
}
}
});
```
Domyślna konfiguracja próbuje połączyć się 8 razy z interwałami 0, 2s, 10s, 30s. W środowiskach z niestabilnym internetem (mobile, hotspot) warto zwiększyć `maxRetries` do 15 i skrócić początkowe opóźnienie do 200 ms. Wykładniczy backoff zapobiega przeciążeniu serwera w przypadku masowej utraty połączeń (np. restart klastra Kubernetes).
Blazor wyświetla domyślny UI podczas próby ponownego połączenia — niebieski pasek na dole ekranu z tekstem "Attempting to reconnect...". Można go zastąpić własnym komponentem przez nadpisanie elementu `#components-reconnect-modal` w CSS lub całkowitą podmianę logiki w JavaScript.
W trybie Auto po utracie połączenia SignalR aplikacja automatycznie przełącza się na WebAssembly (jeśli runtime został już pobrany). Oznacza to, że użytkownik może kontynuować pracę offline — formularz będzie działał lokalnie, a wysyłka danych nastąpi po przywróceniu łączności. To kluczowa przewaga nad czystym Interactive Server w aplikacjach mission-critical.
## Selektywna interaktywność dla optimum wydajności
Parametr `--all-interactive false` wymusza świadome decyzje o interaktywności. Domyślnie każdy komponent renderuje się statycznie — deweloper musi jawnie dodać `@rendermode`, aby włączyć SignalR lub WASM. To podejście drastycznie redukuje payload JavaScript i liczbę otwartych WebSocket.
```csharp
// Components/Pages/Privacy.razor
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]
Privacy Policy
Privacy Policy
Last updated: April 2026
```
Atrybut `[ExcludeFromInteractiveRouting]` gwarantuje, że strona `/privacy` zawsze renderuje się jako czysty HTML, nawet jeśli globalna konfiguracja wymusza Interactive Server. Jest to szczególnie istotne dla stron wymaganych przez regulacje (privacy policy, terms of service, cookie consent) — muszą być dostępne bez JavaScript i ładować się w mniej niż sekundę.
W praktyce większość aplikacji Blazor United składa się z mieszanki stron statycznych (landing, docs, legal), komponentów Interactive Server (dashboardy, formularze CRUD), komponentów Interactive Auto (edytory, chat, real-time collaboration) oraz komponentów WASM (offline forms, heavy client-side calculations). Taka kombinacja minimalizuje koszty infrastruktury, poprawia SEO i zapewnia doskonały UX. Zgodnie z najnowszym asp.net blazor tutorial Microsoft, takie rozłożenie proporcji jest optymalne dla większości aplikacji enterprise.
## Ahead-of-Time compilation dla maksymalnej wydajności
WebAssembly w Blazor domyślnie używa interpretowanego .NET runtime. AOT (Ahead-of-Time) kompiluje assemblies do natywnego WebAssembly podczas budowania, eliminując JIT overhead. Aplikacje zyskują znacząco lepszą wydajność w zamian za dłuższy czas budowania i większy rozmiar bundle.
```bash
# Publish with AOT for production
dotnet publish -c Release -p:RunAOTCompilation=true
```
AOT jest szczególnie opłacalne dla aplikacji z intensywnymi obliczeniami po stronie klienta — edytory graficzne, silniki gier 2D, parsery dużych JSON, kalkulatory finansowe. Dla typowych CRUD-ów różnica będzie niezauważalna, a zwiększony rozmiar bundle może pogorszyć Time to Interactive na wolnych połączeniach.
W .NET 9 AOT wspiera pełne odbicie i większość bibliotek BCL (Base Class Library). Wcześniejsze wersje miały ograniczenia w dynamicznym tworzeniu typów i serializacji JSON — obecnie działa to transparentnie z System.Text.Json i większością ORM.
## Kluczowe wnioski
Blazor United w .NET 9 stanowi przełom w architekturze full-stack aplikacji webowych. Eliminuje konieczność wyboru między wydajnością a interaktywnością, umożliwiając płynne łączenie trybów renderowania w obrębie jednej aplikacji.
- Zunifikowany model programowania — jeden język (C#), jeden runtime, trzy tryby renderowania bez kompromisów
- Optymalna wydajność — statyczny SSR dla SEO, SignalR dla dashboardów, WASM dla offline i heavy computations
- Interactive Auto mode — automatyczne przełączanie z serwera na klient po pobraniu runtime WASM w tle
- Selektywna interaktywność — każdy komponent może mieć własny tryb renderowania, co minimalizuje payload i koszty infrastruktury
- Streaming rendering — progresywne ładowanie HTML redukuje postrzegane opóźnienie
- Resilient connections — konfigurowalne reconnection z wykładniczym backoff i automatyczne fallback do WASM
- AOT compilation — znacząco lepsza wydajność WASM dzięki kompilacji do natywnego kodu
Blazor United to obecnie najbardziej zaawansowany framework full-stack dla deweloperów .NET. Aplikacje budowane w tym modelu osiągają wydajność porównywalną z najlepszymi frameworkami JavaScript, zachowując zalety silnego typowania C# i integracji z ekosystemem .NET. W 2026 roku znajomość blazor united full stack jest standardowym wymaganiem dla senior .NET developers.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/pl/blog/dotnet/dotnet-9-blazor-united-full-stack-development