MAUI Blazor Hybrid u produkciji: Arhitektura, JS interop i performanse (2026)
Praktični vodič za MAUI Blazor Hybrid u produkciji 2026: BlazorWebView na .NET 10, dijeljenje koda kroz RCL, JS interop, native MAUI API-ji iz Razor komponenti, hladno pokretanje i kada NE koristiti Hybrid.
.NET MAUI Blazor Hybrid je pristup gradnji cross-platform mobilnih i desktop aplikacija u kojem se Razor komponente renderiraju unutar native BlazorWebView kontrole, dok istovremeno imaju izravan pristup .NET kodu i native MAUI API-jima bez HTTP sloja. Iskreno, zadnjih devet mjeseci sam isporučio dvije enterprise MAUI Blazor Hybrid aplikacije za klijente u zdravstvu i osiguranju, pa u ovom vodiču dijelim ono što stvarno funkcionira u produkciji u 2026: arhitekturu, JavaScript interop, dijeljenje koda s web frontendom, hladno pokretanje i onih par mjesta gdje sam se ozbiljno opekao.
MAUI Blazor Hybrid u .NET 10 (studeni 2025) koristi BlazorWebView koji radi na WKWebView (iOS/macOS), Android WebView i WebView2 (Windows), bez HTTP sloja između Razor komponenti i .NET runtime-a.
Preporučena arhitektura je Razor Class Library (RCL) sa 80–90% dijeljenih komponenti između MAUI Hybrid i Blazor Server/WASM web klijenta.
Hladno pokretanje na Androidu je 900–1400 ms na srednjem hardveru kad koristite AOT i WebView pre-warming; bez toga očekujte 2.2–3.0 s.
IJSRuntime radi bidirekcijski, ali [JSInvokable] pozive s JS-a treba pažljivo throttlirati na Androidu jer bridge nije besplatan.
Native MAUI API-je (biometrija, kamera, senzori) pozivate izravno iz Razor komponenti, bez potrebe za JavaScript wrapperima.
Za offline aplikacije koristite _content statičke resurse plus SQLite; PWA cache strategije ne rade u Blazor Hybridu jer nema Service Workera.
Što je .NET MAUI Blazor Hybrid?
Blazor Hybrid je runtime model u kojem se Razor komponente izvršavaju izravno u .NET procesu native aplikacije, a njihov HTML rezultat se renderira u ugniježđenom WebView-u. Ključna razlika u odnosu na Blazor Server ili Blazor WebAssembly: nema SignalR veze, nema WASM sandboxa, nema serijalizacije preko mreže. Kad korisnik klikne gumb, poziva se stvarna .NET metoda unutar istog procesa, a ta metoda može slobodno koristiti bilo koji NuGet paket, HttpClient, SecureStorage ili Microsoft.Maui.Media.
U .NET 10 (studeni 2025) BlazorWebView je stabiliziran na sve četiri platforme: iOS i macOS koriste WKWebView, Android koristi androidx.webkit WebView, a Windows koristi Microsoft Edge WebView2. Windows WebView2 ima dinamičko ažuriranje runtime-a preko Evergreen kanala, dok su iOS/Android WebView-i vezani uz verziju OS-a. U praksi to znači da CSS Grid, container queries i view transitions rade na iOS 15+ i Android API 26+ bez polyfilla.
Model komponente je identičan Blazor Web-u: .razor datoteke, @code blokovi, RenderFragment, EventCallback. Razlika je isključivo u hostu i u tome što NavigationManager ne mijenja URL u adresnoj traci (jer je nema) nego samo unutar WebView-a.
Blazor Hybrid vs čisti MAUI vs Blazor WebAssembly
Prije nego što odlučite arhitekturu, vrijedi jasno vidjeti gdje se ova tri pristupa razlikuju u stvarima koje kupci zapravo pitaju: brzina isporuke, veličina paketa, hladno pokretanje i pristup native značajkama.
Kriterij
MAUI Blazor Hybrid
Čisti MAUI (XAML)
Blazor WebAssembly (PWA)
Jezik UI-ja
Razor + HTML/CSS
XAML + C#
Razor + HTML/CSS
Hladno pokretanje (Android, srednji hardver)
0.9–1.4 s (AOT)
0.4–0.8 s
2.5–4.0 s
Veličina APK/IPA
28–42 MB
18–28 MB
N/A (web)
Pristup native API-jima
Direktan (MAUI DI)
Direktan
Ograničen (JS bridge)
Dijeljenje koda s webom
80–90% preko RCL
0–20% (logika)
100%
Offline podrška
Puna (bez SW)
Puna
Djelomična (SW)
App Store distribucija
Da
Da
Ne
Krivulja učenja za web tim
Niska
Visoka (XAML)
Vrlo niska
U mojoj praksi Hybrid pobjeđuje kad postoji postojeći Blazor web klijent koji treba mobilnu verziju, ili kad tim ima duboko HTML/CSS iskustvo a nikoga tko voli XAML. Čisti MAUI pobjeđuje kad su performanse i veličina paketa kritični (npr. aplikacije za područne radnike na starijim uređajima) ili kad UI ovisi o kompleksnim native gestama i animacijama gdje CSS animacije u WebView-u počinju šturi.
Postavljanje projekta i BlazorWebView
Predložak maui-blazor u .NET 10 SDK-u generira ispravnu strukturu, ali za produkciju obično ručno reorganiziram u tri projekta: MyApp.Shared (RCL s komponentama), MyApp.Maui (host) i MyApp.Web (opcionalni Blazor Server/WASM klijent).
Najveća ušteda Blazor Hybrida je stvarno dijeljenje UI koda s webom, ali samo ako svjesno strukturirate ovisnosti. RCL smije referencirati samo Microsoft.AspNetCore.Components.Web i vlastite apstrakcije, ne Microsoft.Maui.* i ne Microsoft.AspNetCore.App. Sve što je platform-specifično apstrahirate iza sučelja koje u MAUI hostu implementirate MAUI API-jima, a u ASP.NET hostu implementirate serverskim ekvivalentima.
// U MyApp.Shared (RCL)
public interface IDeviceStorage
{
Task<string?> GetAsync(string key);
Task SetAsync(string key, string value);
Task RemoveAsync(string key);
}
// U MyApp.Maui
public sealed class MauiSecureStorage : IDeviceStorage
{
public Task<string?> GetAsync(string key) => SecureStorage.GetAsync(key);
public Task SetAsync(string key, string value) => SecureStorage.SetAsync(key, value);
public Task RemoveAsync(string key)
{
SecureStorage.Remove(key);
return Task.CompletedTask;
}
}
// U MyApp.Web
public sealed class ProtectedBrowserStorageAdapter(ProtectedLocalStorage storage)
: IDeviceStorage
{
public async Task<string?> GetAsync(string key)
=> (await storage.GetAsync<string>(key)).Value;
public Task SetAsync(string key, string value) => storage.SetAsync(key, value).AsTask();
public Task RemoveAsync(string key) => storage.DeleteAsync(key).AsTask();
}
Za konzistentan MVVM obrazac unutar Razor komponenti dobar pratitelj je CommunityToolkit.Mvvm; napisao sam poseban vodič za CommunityToolkit.Mvvm u .NET MAUI koji pokazuje kako ObservableObject i [RelayCommand] koristiti unutar Blazor komponenti bez potrebe za StateHasChanged() pozivima.
JavaScript interop u produkciji
Iako Blazor Hybrid ne treba JavaScript za osnovni rad, JS interop ostaje potreban kad želite koristiti postojeće web biblioteke (Chart.js, Leaflet, PDF.js, Video.js) ili kad radite DOM operacije koje Razor komponente ne izražavaju elegantno, poput focus managementa, IntersectionObservera i ResizeObservera. U MAUI Hybrid poziv IJSRuntime.InvokeAsync prolazi kroz native WebView bridge, što na Androidu ima realne troškove od 0.2–0.8 ms po pozivu.
@inject IJSRuntime JS
@implements IAsyncDisposable
<canvas @ref="canvasRef"></canvas>
@code {
private ElementReference canvasRef;
private IJSObjectReference? chart;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender) return;
// Importiramo modul jednom po komponenti
var module = await JS.InvokeAsync<IJSObjectReference>(
"import", "./_content/MyApp.Shared/charts.js");
chart = await module.InvokeAsync<IJSObjectReference>(
"createChart", canvasRef, dataSeries);
}
public async ValueTask DisposeAsync()
{
if (chart is not null)
await chart.InvokeVoidAsync("destroy");
}
}
Za pozive iz JS-a natrag u .NET koristite DotNetObjectReference i [JSInvokable] metode. Ključno pravilo koje sam naučio na teži način: nikad ne pozivajte [JSInvokable] metodu iz requestAnimationFrame ili scroll handlera bez throttlinga. Na Androidu se bridge pozivi serijaliziraju i lako se dovedete u stanje gdje UI thread stoji 300 ms po sekundi. Točno taj bug sam našao u shipping buildu, pa preporučam throttle na 60 fps već pri prvom prototipu.
Poziv native MAUI API-ja iz Razor komponenti
Ovo je razlog zbog kojeg tim uopće bira Hybrid umjesto WebAssembly. Razor komponenta može preko DI-a dobiti bilo koji MAUI servis i pozvati ga direktno, u istom procesu, bez JSON serijalizacije. U praksi to znači da su biometrijska autentifikacija, kamera, GPS, senzori i sigurni storage dostupni sa 5–10 linija koda.
@inject IBiometrics Biometrics
@inject NavigationManager Nav
<button class="btn btn-primary" @onclick="AuthenticateAsync">
Otkljucaj biometrijom
</button>
@if (!string.IsNullOrEmpty(error))
{
<div class="alert alert-danger">@error</div>
}
@code {
private string? error;
private async Task AuthenticateAsync()
{
try
{
var result = await Biometrics.AuthenticateAsync(
reason: "Potvrdite identitet za pristup medicinskoj evidenciji");
if (result.Success)
Nav.NavigateTo("/patients");
else
error = result.ErrorMessage ?? "Autentifikacija nije uspjela.";
}
catch (FeatureNotSupportedException)
{
error = "Uredaj ne podrzava biometriju.";
}
}
}
Isti obrazac vrijedi i za kameru, dohvat kontakata ili slanje push notifikacija. Za sigurnu pohranu tokena i biometriju detaljnije obrađujem u vodiču o sigurnosti .NET MAUI aplikacija. Obrasci su identični bilo da UI slojevi budu XAML ili Razor.
Performanse i hladno pokretanje
Hladno pokretanje MAUI Blazor Hybrid aplikacije na Androidu srednjih performansi (Pixel 6a, Snapdragon 695) obično traje 900–1400 ms u release konfiguraciji s AOT-om. Bez AOT-a i bez WebView pre-warminga očekujte 2.2–3.0 s. Tri stvari koje najviše pomažu, redom po utjecaju:
Full AOT + trimming u .csproj: <RunAOTCompilation>true</RunAOTCompilation> i <PublishTrimmed>true</PublishTrimmed> s TrimMode=full. Pripazite da u ILLink.Descriptors.xml zaštitite tipove koje koristite refleksijom (npr. modele koje serijalizirate u JSON).
Pre-warm WebView na splash screenu. Na Androidu instantiacija WebView-a traje 200–350 ms. Ako ju napravite dok se prikazuje splash, korisnik to ne osjeti.
Lazy-load Razor stranica. Root Routes.razor neka referencira samo home stranicu; ostale stranice ubacite iz zasebnih RCL sklopova s [Route] atributima i AdditionalAssemblies parametrom u Router.
Kritična razlika u odnosu na Blazor WebAssembly PWA: Blazor Hybrid nema Service Worker. To zvuči kao gubitak, ali u praksi ne treba jer aplikacija ionako živi kao native paket. Svi statički resursi (HTML, CSS, JS, slike) su unutar APK/IPA i nema mrežnog dohvata pri startu. Za dinamičke podatke koristite obični SQLite kroz EF Core ili sqlite-net-pcl; obrazac je identičan kao u XAML MAUI aplikacijama.
public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
: DbContext(options)
{
public DbSet<Patient> Patients => Set<Patient>();
public DbSet<Visit> Visits => Set<Visit>();
}
// U MauiProgram.cs
var dbPath = Path.Combine(FileSystem.AppDataDirectory, "app.db");
builder.Services.AddDbContextFactory<AppDbContext>(o =>
o.UseSqlite($"Data Source={dbPath}"));
Za sinkronizaciju s backendom preporučujem outbox obrazac: mutacije se pišu lokalno, a background job (npr. preko IBackgroundJob u iOS-u ili WorkManager-a na Androidu) povremeno pokušava push prema serveru. Sve dodatne detalje o SQLite konfiguraciji, migracijama i konkurentnim čitačima obradio sam u vodiču za lokalnu bazu podataka u .NET MAUI sa SQLite i EF Core.
Debug, DevTools i dijagnostika
Debugiranje Blazor Hybrida traje ne dulje od običnog Blazor Server projekta jednom kad znate koje alate uključiti. Tri kritična koraka:
Enable BlazorWebView developer tools, pozivom builder.Services.AddBlazorWebViewDeveloperTools() unutar #if DEBUG bloka. Bez toga desni klik i "Inspect" u WebView-u ne rade.
Chrome DevTools na Androidu. Spojite uređaj USB-om, otvorite chrome://inspect/#devices i dobit ćete kompletan DOM/Console/Network kao za bilo koji web. Radi i na release buildu ako ostavite android:debuggable="true".
Safari Web Inspector na iOS-u. Na uređaju uključite Settings, Safari, Advanced, Web Inspector, a u macOS Safariju Develop meni. WKWebView instance vidljive su odmah.
Za logiranje iz Razor komponenti koristite standardni ILogger<T> iz Microsoft.Extensions.Logging. Za produkcijsku telemetriju najbolje mi rade Sentry i Application Insights preko Microsoft.ApplicationInsights.WorkerService paketa. Oba imaju .NET klijente koji rade unutar MAUI hosta bez posebnih adaptacija.
Kada NE koristiti Blazor Hybrid
Iako sam ga stavio u produkciju više puta, postoje scenariji gdje bih ga aktivno odgovorio. Prvo, ako aplikacija ima kompleksne native geste (pinch-to-zoom nad mapom sa custom overlayem, drag-and-drop reordering listi na iOS-u), CSS/JS ekvivalenti u WebView-u su vidljivo lošiji od native iOS i Android gestura. Drugo, ako je ciljni uređaj Android verzija 8 ili niže i zahtjev je hladno pokretanje ispod 600 ms, Hybrid to ne stiže bez agresivnog inženjerskog rada.
Treće, ako je tim mješovit (polovica native mobile inženjeri, polovica web), Hybrid stvara laž o dijeljenju koda koja u praksi ne funkcionira jer native tim ionako gura sve u native handlere. U tim slučajevima čisti MAUI s custom handler obrascem je poštenija arhitektura.
Da. Od .NET 8 (studeni 2023) je označen kao produkcijski, a u .NET 10 (studeni 2025) su ispravljeni značajni bug-ovi vezani uz iOS WKWebView i Android WebView memory retention. Osobno sam isporučio dvije enterprise aplikacije u 2026 bez blocker problema; najveći rizici su sada performanse hladnog pokretanja na starijem hardveru i JS interop trošak u vrućim petljama.
Koja je razlika između Blazor Hybrida i običnog MAUI-ja?
Čisti MAUI koristi XAML i native kontrole (UIKit, Android View sustav), dok Blazor Hybrid koristi Razor komponente renderirane u WebView-u. Ista aplikacija u čistom MAUI-ju bit će manja (18–28 MB vs 28–42 MB) i brža za pokretanje, ali dijeljenje koda s webom je vrlo ograničeno. Blazor Hybrid pobjeđuje kad tim ima web iskustvo ili već postoji Blazor web klijent.
Može li Blazor Hybrid raditi offline?
Može, potpuno. Za razliku od Blazor WebAssembly PWA-a, Hybrid ne treba Service Worker jer su svi statički resursi već u APK/IPA paketu. Za dinamičke podatke koristite SQLite preko EF Corea ili sqlite-net-pcl, a za sinkronizaciju outbox obrazac s background jobom.
Je li Blazor Hybrid brži od Blazor WebAssembly na mobitelu?
Da, značajno. Hybrid koristi native .NET runtime umjesto WASM interpretera, pa je startup 2–3× brži i JS interop nema WASM-JS bridge trošak. Tipično hladno pokretanje Hybrida je 0.9–1.4 s (Android, AOT) prema 2.5–4.0 s za Blazor WASM PWA. CPU-intenzivne operacije rade 3–8× brže jer AOT native kod nadmašuje WASM.
Trebam li znati XAML za rad s Blazor Hybridom?
Ne mnogo. Minimalno morate razumjeti MainPage.xaml gdje se hostuje BlazorWebView, splash screen i eventualno tab bar ako aplikacija ima Shell navigaciju. Sav ostali UI je Razor + HTML/CSS. U mojim projektima XAML čini manje od 3% cijelog UI koda.
Caleb has shipped seven mobile apps to the App Store and Play Store over the last nine years, four of them in Xamarin and three in .NET MAUI. He spent five years at a healthcare-tech company in Boston building a clinician-facing iPad app used by roughly 14,000 nurses, then moved to a freelance practice in 2024 focused on enterprise MAUI rollouts for regulated industries.
His writing tends toward the practical: CI pipelines on App Center successors, MSAL token caching across Android lifecycle resets, MAUI Blazor Hybrid in production, and the awkward seams between MAUI and native SDKs that vendors haven't gotten around to wrapping. He runs a small Discord for enterprise MAUI engineers and writes a Friday newsletter from his home in Portland, Maine.
Kompletni migracijski vodič s .NET Upgrade Assistantom, Custom Renderers na Handlers portom, DependencyService zamjenom i CI/CD promjenama za tim koji seli s Xamarin.Forms na .NET MAUI 10 u 2026.
Praktični vodič za CommunityToolkit.Mvvm u .NET MAUI 9 s izvornim generatorima koda, ObservableProperty i RelayCommand atributima, integracijom s Dependency Injectionom i Messengerom za komunikaciju između ViewModela.