Building MAUI Blazor Hybrid Apps: Web UI Meets Native Power
MAUI Blazor Hybrid is one of the more interesting ideas in the .NET ecosystem: take Razor components — the same ones you'd use in a Blazor web app — and host them natively inside a MAUI application via BlazorWebView. Your UI runs in an embedded web view, but your C# code has full access to native device APIs. No JavaScript interop needed for platform features.
When Does This Make Sense?
Blazor Hybrid is a strong choice when you already have a Blazor web application and want to ship a native mobile or desktop version without rewriting the UI in XAML. It's also useful when your team is stronger with HTML/CSS than XAML, or when you need pixel-perfect control that's easier to achieve with CSS.
It's less ideal if you need your app to look and feel exactly like a native platform app. Blazor Hybrid renders web content — it won't automatically adopt platform-specific controls like a XAML-based MAUI app does.
Project Setup
Start with the MAUI Blazor Hybrid template:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<OutputType>Exe</OutputType>
<UseMaui>true</UseMaui>
</PropertyGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui"
Version="9.0.0" />
</Project>
Register the Blazor services in MauiProgram.cs:
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
#endif
builder.Services.AddSingleton<WeatherService>();
return builder.Build();
}
Hosting Razor Components
The MainPage.xaml hosts the BlazorWebView:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainPage">
<BlazorWebView HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app"
ComponentType="{x:Type local:Routes}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</ContentPage>
The wwwroot/index.html is a standard HTML file that loads the Blazor framework:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/app.css" />
</head>
<body>
<div id="app">Loading...</div>
<script src="_framework/blazor.webview.js"></script>
</body>
</html>
Writing Components
Razor components work exactly as they do in Blazor Server or WebAssembly:
@page "/counter"
@inject WeatherService Weather
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Accessing Native APIs from Razor Components
This is where Hybrid shines. Your Razor components can inject services that call native MAUI APIs directly — no JavaScript bridge required:
public class LocationService
{
public async Task<string> GetCurrentLocationAsync()
{
var location = await Geolocation.Default.GetLocationAsync(
new GeolocationRequest(GeolocationAccuracy.Medium));
if (location is null)
return "Location unavailable";
return $"{location.Latitude:F4}, {location.Longitude:F4}";
}
}
Register it in DI and inject it into any Razor component:
@page "/location"
@inject LocationService LocationService
<h2>Your Location</h2>
@if (location is not null)
{
<p>@location</p>
}
<button @onclick="FetchLocation">Get Location</button>
@code {
private string? location;
private async Task FetchLocation()
{
location = await LocationService.GetCurrentLocationAsync();
}
}
Your Razor component is running inside the native app process. It can access the file system, camera, sensors, and every other MAUI API without any interop overhead.
Sharing Components Between Web and Hybrid
A practical architecture for code sharing uses a Razor Class Library (RCL):
MyApp.Components/ <-- Razor Class Library (shared)
MyApp.Web/ <-- Blazor Server or WASM project
MyApp.Maui/ <-- MAUI Blazor Hybrid project
Both the web and MAUI projects reference the shared component library. Platform-specific services are injected through DI — the web project provides web implementations, the MAUI project provides native ones.
// Shared interface
public interface IShareService
{
Task ShareText(string text);
}
// MAUI implementation — uses native share sheet
public class MauiShareService : IShareService
{
public async Task ShareText(string text)
{
await Share.Default.RequestAsync(new ShareTextRequest
{
Text = text,
Title = "Share via"
});
}
}
// Web implementation — uses clipboard
public class WebShareService : IShareService
{
public Task ShareText(string text)
{
// Use JS interop to copy to clipboard
return Task.CompletedTask;
}
}
Performance Considerations
The embedded web view adds some overhead compared to pure MAUI XAML. On older Android devices, you may notice slightly slower rendering for complex UIs. Profile on real hardware rather than emulators to get an accurate picture.
Blazor Hybrid is not Electron — your C# code runs natively, not in a JavaScript runtime. The web view only handles rendering. This means your business logic runs at full native speed.
If you have Blazor expertise and need to ship native apps, Hybrid is a genuinely productive approach. You get native distribution, native APIs, and a familiar web development model.