Host a Blazor web app in a .NET MAUI app using BlazorWebView
The .NET Multi-platform App UI (.NET MAUI) BlazorWebView is a control that enables you to host a Blazor web app in your .NET MAUI app. These apps, known as Blazor Hybrid apps, enable a Blazor web app to be integrated with platform features and UI controls. The BlazorWebView control can be added to any page of a .NET MAUI app, and pointed to the root of the Blazor app. The Razor components run natively in the .NET process and render web UI to an embedded web view control. In .NET MAUI, Blazor Hybrid apps can run on all the platforms supported by .NET MAUI.
BlazorWebView defines the following properties:
- HostPage, of type
string?
, which defines the root page of the Blazor web app. - RootComponents, of type
RootComponentsCollection
, which specifies the collection of root components that can be added to the control. - StartPath, of type
string
, which defines the path for initial navigation within the Blazor navigation context when the Blazor component is finished loading.
The RootComponent class defines the following properties:
- Selector, of type
string?
, which defines the CSS selector string that specifies where in the document the component should be placed. - ComponentType, of type
Type?
, which defines the type of the root component. - Parameters, of type
IDictionary<string, object?>?
, which represents an optional dictionary of parameters to pass to the root component.
In addition, BlazorWebView defines the following events:
- BlazorWebViewInitializing, with an accompanying
BlazorWebViewInitializingEventArgs
object, which is raised before the BlazorWebView is initialized. This event enables customization of the BlazorWebView configuration. - BlazorWebViewInitialized, with an accompanying
BlazorWebViewInitializedEventArgs
object, which is raised after the BlazorWebView is initialized but before any component has been rendered. This event enables retrieval of the platform-specific web view instance. - UrlLoading, with an accompanying
UrlLoadingEventArgs
object, is raised when a hyperlink is clicked within a BlazorWebView. This event enables customization of whether a hyperlink is opened in the BlazorWebView, in an external app, or whether the URL loading attempt is cancelled.
Existing Razor components can be used in a .NET MAUI Blazor app by moving the code into the app, or by referencing an existing class library or package that contains the component. For more information, see Reuse Razor components in ASP.NET Core Blazor Hybrid.
Browser developer tools can be used to inspect .NET MAUI Blazor apps. For more information, see Use browser developer tools with ASP.NET Core Blazor Hybrid.
Note
While Visual Studio installs all the required tooling to develop .NET MAUI Blazor apps, end users of .NET MAUI Blazor apps on Windows must install the WebView2 runtime.
For more information about Blazor Hybrid apps, see ASP.NET Core Blazor Hybrid.
Create a .NET MAUI Blazor app
A .NET MAUI Blazor app can be created in Visual Studio by the .NET MAUI Blazor app template:
This project template creates a multi-targeted .NET MAUI Blazor app that can be deployed to Android, iOS, macOS, and Windows. For step-by-step instructions on creating a .NET MAUI Blazor app, see Build a .NET MAUI Blazor app.
The BlazorWebView created by the project template is defined in MainPage.xaml, and points to the root of the Blazor app:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BlazorWebViewDemo"
x:Class="BlazorWebViewDemo.MainPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<BlazorWebView HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</ContentPage>
The root Razor component for the app is in Main.razor, which Razor compiles into a type named Main
in the application's root namespace. The rest of the Razor components are in the Pages and Shared project folders, and are identical to the components used in the default Blazor web template. Static web assets for the app are in the wwwroot folder.
Add a BlazorWebView to an existing app
The process to add a BlazorWebView to an existing .NET MAUI app is as follows:
Add the Razor SDK,
Microsoft.NET.Sdk.Razor
to your project by editing its first line of the CSPROJ project file:<Project Sdk="Microsoft.NET.Sdk.Razor">
The Razor SDK is required to build and package projects containing Razor files for Blazor projects.
Add the root Razor component for the app to the project.
Add your Razor components to project folders named Pages and Shared.
Add your static web assets to a project folder named wwwroot.
Add any optional _Imports.razor files to your project.
Add a BlazorWebView to a page in your .NET MAUI app, and point it to the root of the Blazor app:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:MyBlazorApp" x:Class="MyBlazorApp.MainPage"> <BlazorWebView HostPage="wwwroot/index.html"> <BlazorWebView.RootComponents> <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" /> </BlazorWebView.RootComponents> </BlazorWebView> </ContentPage>
Modify the
CreateMauiApp
method of yourMauiProgram
class to register the BlazorWebView control for use in your app. To do this, on the IServiceCollection object, call theAddMauiBlazorWebView
method to add component web view services to the services collection:public static class MauiProgram { 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 // Register any app services on the IServiceCollection object // e.g. builder.Services.AddSingleton<WeatherForecastService>(); return builder.Build(); } }
This code also enables developer tools on the underlying WebView controls, when the app is running in debug configuration.
Access scoped services from native UI
BlazorWebView has a TryDispatchAsync method that can call a specified Action<ServiceProvider>
asynchronously and pass in the scoped services available in Razor components. This enables code from the native UI to access scoped services such as NavigationManager:
private async void OnMyMauiButtonClicked(object sender, EventArgs e)
{
var wasDispatchCalled = await blazorWebView.TryDispatchAsync(sp =>
{
var navMan = sp.GetRequiredService<NavigationManager>();
navMan.CallSomeNavigationApi(...);
});
if (!wasDispatchCalled)
{
// Consider what to do if it the dispatch fails - that's up to your app to decide.
}
}
Diagnosing issues
BlazorWebView has built-in logging that can help you diagnose issues in your Blazor Hybrid app. There are two steps to enable this logging:
- Enable BlazorWebView and related components to log diagnostic information.
- Configure a logger to write the log output to where you can view it.
For more information about logging, see Logging in C# and .NET.
Enable BlazorWebView logging
All logging configuration can be performed as part of service registration in the dependency injection system. To enable maximum logging for BlazorWebView and related components under the Microsoft.AspNetCore.Components.WebView namespace, add the following code to where your app's services are registered:
services.AddLogging(logging =>
{
logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
});
Alternatively, to enable maximum logging for every component that uses Microsoft.Extensions.Logging, you could use the following code:
services.AddLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
});
Configure logging output and viewing the output
After configuring components to write log information you need to configure where the loggers should write the logs to, and then view the log output.
The Debug logging providers write the output using Debug
statements, and the output can be viewed from Visual Studio.
To configure the Debug logging provider, first add a reference in your project to the Microsoft.Extensions.Logging.Debug
NuGet package. Then, register the provider inside the call to AddLogging that you added in the previous step by calling the AddDebug extension method:
services.AddLogging(logging =>
{
logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
logging.AddDebug();
});
When you run the app from Visual Studio (with debugging enabled), you can view the debug output in Visual Studio's Output window.
Play inline video on iOS
To play inline video in a Blazor hybrid app on iOS, in a BlazorWebView, you should:
Set the UrlLoadingStrategy property to
OpenInWebView
. This can be accomplished in the event handler for the UrlLoading event:private void BlazorUrlLoading(object? sender, UrlLoadingEventArgs e) { #if IOS e.UrlLoadingStrategy = UrlLoadingStrategy.OpenInWebView; #endif }
Ensure that the
AllowsInlineMediaPlayback
property in aConfiguration
object is set totrue
. This can be accomplished in the event handler for the BlazorWebViewInitializing event:private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) { #if IOS e.Configuration.AllowsInlineMediaPlayback = true; #endif }
Disposal deadlocks on Android
By default, BlazorWebView fires and forgets the async disposal of the underlying WebViewManager
. This reduces the potential for disposal deadlocks to occur on Android.
Warning
This fire-and-forget default behavior means that disposal can return before all objects are disposed, which can cause behavioral changes in your app. The items that are disposed are partially Blazor's own internal types, but also app-defined types such as scoped services used within the BlazorWebView portion of your app.
To opt out of this behavior, you should configure your app to block on dispose via an AppContext switch in the CreateMauiApp
method in your MauiProgram
class:
AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", false);
If your app is configured to block on dispose via this switch, BlazorWebView performs async-over-sync disposal, which means that it blocks the thread until the async disposal is complete. However, this can cause deadlocks if the disposal needs to run code on the same thread (because the thread is blocked while waiting).
Host content using the legacy behavior on iOS and Mac Catalyst
On iOS and Mac Catalyst 18, the default behavior for hosting content in a BlazorWebView has changed to localhost
. The internal 0.0.0.1
address used to host content no longer works and results in the BlazorWebView not loading any content and rendering as an empty rectangle.
To opt into using the 0.0.0.1
address, add the following code to the CreateMauiApp
method in MauiProgram.cs:
// Set this switch to use the LEGACY behavior of always using 0.0.0.1 to host BlazorWebView
AppContext.SetSwitch("BlazorWebView.AppHostAddressAlways0000", true);