Blazor Hybrid v .NET MAUI 9: Praktický průvodce sdílením webového a mobilního UI

Kompletní průvodce Blazor Hybrid v .NET MAUI 9. Naučte se sdílet Razor komponenty mezi webem a mobilem, integrovat nativní API a optimalizovat výkon WebView na pomalých Android zařízeních.

Blazor Hybrid v .NET MAUI 9: Guide

Aktualizováno: 25. června 2026

Blazor Hybrid v .NET MAUI umožňuje hostovat Razor komponenty uvnitř nativní mobilní aplikace pomocí komponenty BlazorWebView, takže můžete sdílet stejný webový kód, styly a komponenty mezi webovou a mobilní aplikací, a přitom mít plný přístup k nativním API zařízení. V tomhle průvodci postavíme funkční Blazor Hybrid aplikaci v .NET 9, ukážeme si registraci služeb, sdílení komponent přes Razor Class Library, JavaScript ↔ .NET interop a strategie pro ladění výkonu na pomalých Android zařízeních.

Pro kontext: v posledním projektu pro terénní techniky jsme tímhle způsobem sdíleli zhruba 80 % UI kódu mezi webovým dashboardem a mobilní aplikací. Pár věcí mě překvapilo, k těm se postupně dostaneme.

  • Blazor Hybrid renderuje Razor komponenty v nativním WebView, ale veškerá logika běží na .NET runtime. Žádné WebAssembly, žádný HTTP server.
  • Pro maximální sdílení kódu vložte komponenty do Razor Class Library (RCL) a nasdílejte je mezi MAUI projekt a Blazor Server / WebAssembly web.
  • Komponenta BlazorWebView sdílí IServiceProvider s MAUI hostem, takže služby registrované v MauiProgram jsou dostupné v Razor komponentách přes @inject.
  • JavaScript interop přes IJSRuntime a atribut [JSInvokable] umožňuje obousměrnou komunikaci s nativními API skrze MAUI Essentials.
  • První render trvá 300–800 ms kvůli inicializaci WebView. Řešením je preloading na splash screenu a používání @key pro stabilní stromy.
  • Hot Reload v .NET 9 funguje pro Razor komponenty i v MAUI projektu, ale vyžaduje dotnet watch spuštěný z command line.

Co je Blazor Hybrid a jak funguje v .NET MAUI?

Blazor Hybrid je hostovací model, ve kterém běží Razor komponenty přímo na .NET runtime mobilní aplikace a renderují HTML do platformního WebView (WKWebView na iOS/macOS, WebView2 postavený na Edge Chromium na Windows a Android.Webkit.WebView na Androidu). Na rozdíl od Blazor WebAssembly se .NET nepřevádí do WASM a neběží v sandboxu prohlížeče. Místo toho komponenty komunikují s WebView přes IPC kanál a získávají přímý přístup ke všem nativním API platformy.

V .NET MAUI je integračním bodem komponenta BlazorWebView, kterou umístíte na libovolnou ContentPage. WebView pak slouží jako renderovací surface. DOM strom je generován z C# a Razor markupu, ale samotné výpočty, validace a stavová logika se odehrávají v nativním procesu. Tento přístup je popsán v oficiální dokumentaci ASP.NET Core Blazor Hybrid a stal se preferovaným modelem pro týmy migrující z Xamarin.Forms, které už investovaly do webových komponent.

Hlavní motivací pro volbu Blazor Hybrid je sdílení UI vrstvy mezi webem a mobilem. Pokud váš tým udržuje administrátorský dashboard v Blazor Serveru, můžete stejné stránky bez úprav zobrazit v mobilní MAUI aplikaci pro terénní techniky. Stačí jen vyměnit hostovací projekt. To je výhoda, kterou XAML přístup principiálně nabídnout nemůže.

Vytvoření Blazor Hybrid projektu krok za krokem

Pro start potřebujete .NET 9 SDK (verze 9.0.100 nebo novější) a MAUI workload. Šablona maui-blazor vytvoří kompletní projekt s nakonfigurovaným BlazorWebView, vzorovou stránkou a wwwroot složkou.

# Instalace nejnovějšího MAUI workloadu pro .NET 9
dotnet workload install maui

# Vytvoření Blazor Hybrid projektu
dotnet new maui-blazor -n CompanyApp -o ./CompanyApp

# Spuštění na Androidu
cd CompanyApp
dotnet build -t:Run -f net9.0-android

Šablona vygeneruje soubor MauiProgram.cs s registrací MauiBlazorWebViewDeveloperTools (jen pro Debug), AddMauiBlazorWebView() a samotnou hlavní stránku MainPage.xaml obsahující BlazorWebView. Klíčové je, že HostPage ukazuje na wwwroot/index.html, což je jediný statický HTML soubor, který WebView načte při spuštění.

// MauiProgram.cs: registrace Blazor Hybrid runtime
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // Registrace Blazor runtime, zpřístupní BlazorWebView komponentu
        builder.Services.AddMauiBlazorWebView();

#if DEBUG
        // Aktivuje WebView DevTools na všech platformách
        builder.Services.AddBlazorWebViewDeveloperTools();
        builder.Logging.AddDebug();
#endif

        // Sdílené aplikační služby, dostupné v Razor i v MAUI
        builder.Services.AddSingleton<IWeatherService, OpenMeteoService>();
        builder.Services.AddScoped<UserStateContainer>();

        return builder.Build();
    }
}

Sdílení kódu mezi webovou a mobilní aplikací přes RCL

Pro produkční sdílení komponent je standardní vzor jednoduchý: vytvořit Razor Class Library (RCL) obsahující všechny komponenty, stránky, CSS a obrázky. Tento projekt pak referencuje jak Blazor Server / WebAssembly aplikace, tak MAUI Blazor projekt. Změna komponenty v RCL se okamžitě projeví ve všech hostech.

# Vytvoření RCL pro sdílené komponenty
dotnet new razorclasslib -n CompanyApp.Shared -o ./CompanyApp.Shared

# Reference z MAUI projektu
dotnet add ./CompanyApp/CompanyApp.csproj reference ./CompanyApp.Shared/CompanyApp.Shared.csproj

# Reference z webové aplikace
dotnet add ./CompanyApp.Web/CompanyApp.Web.csproj reference ./CompanyApp.Shared/CompanyApp.Shared.csproj

Důležitý detail (a tady jsem se kdysi napálil): cílový framework RCL musí být net9.0, nikoli net9.0-android nebo jiná platforma. Pokud potřebujete v komponentě podmíněně volat nativní API, použijte injektovanou abstrakci přes rozhraní, jehož implementaci poskytne každý host samostatně.

// CompanyApp.Shared/Services/IPlatformInfo.cs
public interface IPlatformInfo
{
    string DeviceType { get; }
    bool IsOffline { get; }
    Task<string?> ScanBarcodeAsync();
}

// CompanyApp/Platforms/MauiPlatformInfo.cs: MAUI implementace
public sealed class MauiPlatformInfo : IPlatformInfo
{
    public string DeviceType => DeviceInfo.Current.Idiom.ToString();
    public bool IsOffline => Connectivity.NetworkAccess != NetworkAccess.Internet;

    public async Task<string?> ScanBarcodeAsync()
    {
        // ZXing.Net.Maui nebo jiná knihovna pro skenování
        return await BarcodeScanner.ScanAsync();
    }
}

Komponenta v RCL pak nikdy přímo nereferencuje Microsoft.Maui namespace, pracuje pouze s rozhraním IPlatformInfo. To znamená, že stejnou komponentu lze hostovat i v čistě webovém prostředí, kde implementace IPlatformInfo vrátí například "Browser" a vyhodí NotSupportedException pro skener.

Dependency Injection a sdílený service container

Jednou z největších výhod Blazor Hybrid je sdílený DI container. Veškeré služby registrované v MauiProgram.CreateMauiApp() jsou dostupné jak v MAUI XAML stránkách, tak v Razor komponentách uvnitř BlazorWebView. Nemusíte tedy duplikovat registrace ani synchronizovat dva nezávislé kontejnery.

Důležitý rozdíl proti čistému Blazor Serveru je v chápání Scoped služeb. V Blazor Hybrid se "scope" vytváří při každém renderingu BlazorWebView, takže typicky vznikne jeden scope na celou životnost aplikace, pokud nepoužíváte více BlazorWebView komponent. Proto je AddScoped v praxi téměř ekvivalentní AddSingleton. Tenhle přístup detailně rozebírá náš článek o MVVM v .NET MAUI s CommunityToolkit.Mvvm, kde se DI hojně používá pro injektování view modelů.

@* Counter.razor: Razor komponenta s injektovanou službou *@
@page "/counter"
@inject IPlatformInfo Platform
@inject IWeatherService Weather

<h1>Počítadlo</h1>
<p>Zařízení: @Platform.DeviceType</p>
<p>Stav sítě: @(Platform.IsOffline ? "Offline" : "Online")</p>

@if (forecast is not null)
{
    <p>Teplota: @forecast.TemperatureC °C</p>
}

<button class="btn btn-primary" @onclick="LoadAsync">Načíst počasí</button>

@code {
    private WeatherForecast? forecast;

    private async Task LoadAsync()
    {
        forecast = await Weather.GetCurrentAsync();
    }
}

Jak volat nativní MAUI API z Blazor komponent

Existují tři způsoby, jak volat nativní platformní kód z Razor komponenty:

  1. Přes injektovanou službu. Nejčistší cesta. Definujte rozhraní v RCL, implementaci v MAUI projektu (může používat Geolocation.Default, SecureStorage, MediaPicker, atd.) a injektujte do komponenty.
  2. Přes IJSRuntime + custom JS handler. Vhodné, když potřebujete JavaScript API prohlížeče (např. navigator.clipboard) a nechcete sahat na nativní vrstvu.
  3. Přes [JSInvokable] z webu do .NET. Pro callbacky z webových knihoven (např. Chart.js eventy) zpět do C# kódu.
// IGeolocationBridge.cs: abstrakce v RCL
public interface IGeolocationBridge
{
    Task<(double Lat, double Lon)?> GetCurrentLocationAsync(CancellationToken ct);
}

// MauiGeolocationBridge.cs: implementace v MAUI projektu
public sealed class MauiGeolocationBridge : IGeolocationBridge
{
    public async Task<(double Lat, double Lon)?> GetCurrentLocationAsync(CancellationToken ct)
    {
        var status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
        if (status != PermissionStatus.Granted) return null;

        var location = await Geolocation.Default.GetLocationAsync(
            new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10)),
            ct);

        return location is null ? null : (location.Latitude, location.Longitude);
    }
}

// Registrace v MauiProgram.cs
builder.Services.AddSingleton<IGeolocationBridge, MauiGeolocationBridge>();

Pro JavaScript interop platí klasický Blazor vzor: vložte JS soubor do wwwroot/js/, načtěte ho v index.html a v komponentě injektujte IJSRuntime:

@inject IJSRuntime JS

<button @onclick="CopyAsync">Kopírovat</button>

@code {
    private async Task CopyAsync()
    {
        // Volání JS funkce window.appBridge.copyToClipboard
        await JS.InvokeVoidAsync("appBridge.copyToClipboard", "Hello z MAUI");
    }
}

Blazor Hybrid vs Blazor Server vs Blazor WebAssembly

Tým, který se rozhoduje mezi hostovacími modely, by měl porovnat tři klíčové dimenze: kde běží .NET runtime, jak rychle se aplikace načítá a jaký má přístup k zařízení. Následující tabulka shrnuje hlavní rozdíly:

VlastnostBlazor HybridBlazor ServerBlazor WebAssembly
Kde běží .NETNativně v procesu mobilní aplikaceNa serveru (ASP.NET Core)V prohlížeči (WASM sandbox)
Velikost download~30 MB (binárka)~150 KB (SignalR)~2–10 MB (WASM)
První render300–800 ms (po WebView init)50 ms (server roundtrip)1–3 s (WASM warmup)
Offline režimPlně podporovánVyžaduje připojeníPlně podporován (PWA)
Přístup k nativním APIPlný (přes MAUI)ŽádnýPouze přes JS bridge
App Store distribuceAno (Google Play, App Store)Ne (jen web)Pouze přes PWA wrapper
Sdílení komponentS Blazor Server/WASM přes RCLnenínení

Praktické pravidlo: pokud potřebujete být v App Storu, mít offline funkčnost a přístup ke kameře, GPS a Bluetooth, jednoznačně volte Blazor Hybrid. Pokud aplikace běží jen v browseru a chcete maximální výkon při prvním načtení, je Blazor Server vhodnější. Blazor WebAssembly má smysl pro PWA, které nepotřebují server a hostují se ze statického CDN.

Jak optimalizovat výkon Blazor Hybrid aplikace

Hlavní výkonová bolest Blazor Hybrid je doba prvního renderu. WebView2 na Windows se inicializuje za 200–400 ms, WKWebView za 150–300 ms, ale Android WebView může na low-end zařízeních trvat i 800 ms. (Tenhle bod jsem si zažil na vlastní kůži při testování na pětileté Samsung tabletě, kde jsme dojeli na 1,1 s.) Tady jsou ověřené techniky pro zrychlení:

  • Preload WebView na splash screenu. Vytvořte skrytou instanci BlazorWebView v App.xaml.cs hned po startu, aby byl WebView "teplý" než uživatel dorazí na první stránku.
  • AOT kompilace. Pro release buildy zapněte <RunAOTCompilation>true</RunAOTCompilation> v csproj. Zkracuje to JIT čas o 30–50 %.
  • Trimming a R2R. <PublishTrimmed>true</PublishTrimmed> sníží velikost binárky o 40 % a tím i čas načítání assemblies do paměti.
  • Stabilní @key v listech. Bez něj Blazor přerendrovává celé seznamy při změně objednání.
  • Vyhněte se StateHasChanged() v cyklech. Používejte ho jen jednou po dokončení batch operace.

Pro pokročilou optimalizaci doporučujeme paralelní čtení článku Offline-first architektura v .NET MAUI, který se zabývá obecnými technikami jako SQLite sync, lazy loading a profilování startupu (všechny jsou plně aplikovatelné i na Blazor Hybrid projekty).

Časté problémy a jejich řešení

Při implementaci Blazor Hybrid narazíte v praxi nejčastěji na tyto čtyři problémy. Všechny mají dokumentovaná řešení:

  1. Statické soubory nejsou nalezeny. Pokud komponenta v RCL odkazuje na img/logo.png, musíte v MAUI projektu cestu upravit na _content/CompanyApp.Shared/img/logo.png. Razor Class Libraries servírují assety přes virtuální cestu _content/{AssemblyName}/.
  2. Hot Reload nefunguje pro Razor. Spusťte aplikaci přes dotnet watch run -t:Run -f net9.0-android z příkazové řádky, nikoli z Visual Studia. F5 v IDE v současnosti Hot Reload pro RCL nezachytává spolehlivě.
  3. Aplikace padá s "Microsoft.AspNetCore.Components.WebView not found". Chybí balíček Microsoft.AspNetCore.Components.WebView.Maui a registrace AddMauiBlazorWebView().
  4. iOS build selhává s Mono linker errors. Přidejte do csproj <PublishTrimmed>false</PublishTrimmed> pro debug konfiguraci nebo nakonfigurujte TrimmerRootDescriptor pro vaši RCL.

Detailní troubleshooting matice je v .NET MAUI wiki na GitHubu, kde tým aktivně aktualizuje známé problémy a workaroundy pro každou release.

Často kladené otázky

Mohu používat Tailwind CSS nebo Bootstrap v Blazor Hybrid?

Ano, jakákoli CSS knihovna funguje v Blazor Hybrid stejně jako v Blazor Serveru. Tailwind doporučujeme integrovat přes tailwindcss CLI a generovat app.css jako pre-build krok. Bootstrap je v šabloně maui-blazor zahrnut ve výchozím stavu.

Funguje SignalR a Server-Sent Events v Blazor Hybrid?

Ano, SignalR plně funguje. Komponenty se připojují k vzdálenému hubu stejným způsobem jako z webového Blazoru. Žádný speciální setup pro mobil není potřeba, ale počítejte s tím, že na Androidu se WebSocket spojení přerušuje při přechodu do pozadí; je vhodné implementovat HubConnection.Closed handler s automatickým reconnectem.

Jak velkou aplikaci dostanu na App Store?

Typická Blazor Hybrid aplikace má po AOT kompilaci a trimmingu 25–35 MB pro Android (APK) a 40–55 MB pro iOS (IPA). To je srovnatelné s React Native nebo Flutter aplikacemi. App Store nemá žádný limit, který by to porušilo, limit pro App Bundle download přes mobilní data je 200 MB.

Mohu sdílet Blazor Hybrid kód s WPF nebo WinForms?

Ano. BlazorWebView existuje i v balíčcích Microsoft.AspNetCore.Components.WebView.Wpf a Microsoft.AspNetCore.Components.WebView.WindowsForms. Pokud udržujete legacy desktop aplikaci, můžete do ní embeddovat stejné Razor komponenty jako do MAUI mobilní aplikace.

Jak debugovat JavaScript v BlazorWebView?

V debug buildu zavolejte AddBlazorWebViewDeveloperTools() a poté na Androidu otevřete chrome://inspect v Chrome desktop, na iOS Safari → Develop menu, na Windows klikněte pravým tlačítkem na WebView a vyberte „Inspect". Plný Chrome DevTools je k dispozici včetně Network, Console a Sources.

Editorial Team
O Autorovi Editorial Team

Our team of expert writers and editors.