diff --git a/src/BlazorWebView/src/Maui/MauiBlazorWebViewBuilderExtensions.cs b/src/BlazorWebView/src/Maui/MauiBlazorWebViewBuilderExtensions.cs
new file mode 100644
index 000000000000..ad9553b07fc5
--- /dev/null
+++ b/src/BlazorWebView/src/Maui/MauiBlazorWebViewBuilderExtensions.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace Microsoft.AspNetCore.Components.WebView.Maui
+{
+ ///
+ /// Extension methods for .
+ ///
+ public static class MauiBlazorWebViewBuilderExtensions
+ {
+ ///
+ /// Registers a custom handler for , replacing the default
+ /// registered by
+ /// .
+ /// This allows custom platform backends to provide their own BlazorWebView handler
+ /// while reusing all shared service registrations.
+ ///
+ ///
+ /// Replacement is "last-registration-wins" through the underlying MAUI handler collection.
+ /// Call this method after AddMauiBlazorWebView() so the custom handler
+ /// overrides the default registration. If a downstream library calls
+ /// AddMauiBlazorWebView() again later in the pipeline, that subsequent default
+ /// registration will silently re-override this custom handler — call this method last,
+ /// after every other library's MAUI Blazor configuration, when composing multiple sources.
+ ///
+ /// The custom handler type to use for .
+ /// Must have a public parameterless constructor.
+ /// The .
+ /// The for chaining.
+ public static IMauiBlazorWebViewBuilder UsePlatformHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler>(
+ this IMauiBlazorWebViewBuilder builder)
+ where THandler : IViewHandler, new()
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ builder.Services.ConfigureMauiHandlers(handlers =>
+ handlers.AddHandler());
+ return builder;
+ }
+
+ ///
+ /// Registers a custom handler for using a factory method,
+ /// replacing the default registered by
+ /// .
+ /// Use this overload for handlers that lack a public parameterless constructor or that
+ /// need to pull dependencies from the MAUI handler service container at construction time.
+ ///
+ ///
+ /// The passed to is the MAUI
+ /// handler factory's service provider, not the application's root .
+ /// It can resolve services that were registered on the handler collection (via
+ /// ConfigureMauiHandlers); it cannot resolve arbitrary services from the app's
+ /// . The same call-ordering rule as
+ /// applies — call this
+ /// method after AddMauiBlazorWebView() (and after any later re-invocations from
+ /// downstream libraries) so the custom handler is the last registration to win.
+ ///
+ /// The .
+ /// A factory function that creates the handler instance.
+ /// The argument is the MAUI handler factory's service provider
+ /// (see remarks).
+ /// The for chaining.
+ public static IMauiBlazorWebViewBuilder UsePlatformHandler(
+ this IMauiBlazorWebViewBuilder builder,
+ Func factory)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(factory);
+ builder.Services.ConfigureMauiHandlers(handlers =>
+ handlers.AddHandler(factory));
+ return builder;
+ }
+ }
+}
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net-android/PublicAPI.Unshipped.txt
index 51e039fc88da..038b7546d70e 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net-android/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net-android/PublicAPI.Unshipped.txt
@@ -1,2 +1,5 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
override Microsoft.AspNetCore.Components.WebView.Maui.BlazorWebViewHandler.ConnectHandler(Android.Webkit.WebView! platformView) -> void
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net-ios/PublicAPI.Unshipped.txt
index 7dc5c58110bf..79428f930adf 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net-ios/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net-ios/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
index 7dc5c58110bf..79428f930adf 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net-tizen/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
index 7dc5c58110bf..79428f930adf 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net-windows/PublicAPI.Unshipped.txt
index 7dc5c58110bf..79428f930adf 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net-windows/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net-windows/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
diff --git a/src/BlazorWebView/src/Maui/PublicAPI/net/PublicAPI.Unshipped.txt b/src/BlazorWebView/src/Maui/PublicAPI/net/PublicAPI.Unshipped.txt
index 7dc5c58110bf..79428f930adf 100644
--- a/src/BlazorWebView/src/Maui/PublicAPI/net/PublicAPI.Unshipped.txt
+++ b/src/BlazorWebView/src/Maui/PublicAPI/net/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder, System.Func! factory) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
+static Microsoft.AspNetCore.Components.WebView.Maui.MauiBlazorWebViewBuilderExtensions.UsePlatformHandler(this Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder! builder) -> Microsoft.AspNetCore.Components.WebView.Maui.IMauiBlazorWebViewBuilder!
diff --git a/src/BlazorWebView/tests/DeviceTests/Elements/BlazorWebViewTests.Services.cs b/src/BlazorWebView/tests/DeviceTests/Elements/BlazorWebViewTests.Services.cs
index 9a4c8fd9132a..b29a23d27192 100644
--- a/src/BlazorWebView/tests/DeviceTests/Elements/BlazorWebViewTests.Services.cs
+++ b/src/BlazorWebView/tests/DeviceTests/Elements/BlazorWebViewTests.Services.cs
@@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebView.Maui;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Maui.Hosting;
using Microsoft.Maui.MauiBlazorWebView.DeviceTests.Components;
using WebViewAppShared;
using Xunit;
@@ -119,4 +120,55 @@ await Assert.ThrowsAsync(async () =>
});
});
}
+
+ [Fact]
+ public void UsePlatformHandlerGenericReplacesDefaultBlazorWebViewHandler()
+ {
+ // Verifies that the public UsePlatformHandler() extension on IMauiBlazorWebViewBuilder
+ // actually replaces the default BlazorWebViewHandler registered by AddMauiBlazorWebView()
+ // for the IBlazorWebView service type. The two HostBuilderHandlerTests in Core verify the
+ // underlying ConfigureMauiHandlers replacement mechanism with stub types; this test exercises
+ // the new public API surface end-to-end with the real BlazorWebView/IBlazorWebView types.
+ var builder = MauiApp.CreateBuilder();
+ builder.Services.AddMauiBlazorWebView()
+ .UsePlatformHandler();
+ using var app = builder.Build();
+
+ var handlersFactory = app.Services.GetRequiredService();
+ Assert.Equal(typeof(CustomBlazorWebViewHandlerStub), handlersFactory.GetHandlerType(typeof(BlazorWebView)));
+ }
+
+ [Fact]
+ public void UsePlatformHandlerFactoryReplacesDefaultBlazorWebViewHandler()
+ {
+ // Companion to UsePlatformHandlerGenericReplacesDefaultBlazorWebViewHandler — verifies the
+ // factory overload (Func) also replaces the default handler.
+ // The factory overload registers a different ServiceDescriptor shape (ImplementationFactory
+ // rather than ImplementationType), so GetHandlerType returns null here; we resolve through
+ // GetHandler instead and assert the produced instance type.
+ var builder = MauiApp.CreateBuilder();
+ var factoryWasCalled = false;
+ builder.Services.AddMauiBlazorWebView()
+ .UsePlatformHandler(_ =>
+ {
+ factoryWasCalled = true;
+ return new CustomBlazorWebViewHandlerStub();
+ });
+ using var app = builder.Build();
+
+ var handlersFactory = app.Services.GetRequiredService();
+ var handler = handlersFactory.GetHandler(typeof(BlazorWebView));
+
+ Assert.True(factoryWasCalled, "Factory delegate should have been invoked when the handler was resolved.");
+ Assert.IsType(handler);
+ }
+
+ private class CustomBlazorWebViewHandlerStub : BlazorWebViewHandler
+ {
+ // Marker subclass used only to prove that UsePlatformHandler replaced the default
+ // BlazorWebViewHandler registration. Inheriting from BlazorWebViewHandler keeps the
+ // IViewHandler contract honored on every device-test target framework without forcing
+ // us to reimplement the full handler surface.
+ public CustomBlazorWebViewHandlerStub() { }
+ }
}
diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs
index ded85f07a562..f2b2d570bbd0 100644
--- a/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs
+++ b/src/Core/tests/UnitTests/Hosting/HostBuilderHandlerTests.cs
@@ -276,5 +276,39 @@ public void HostBuilderCannotResolveHandlerTypeForServiceRegisteredWithFactory()
Type handlerType = mauiHandlersFactory.GetHandlerType(typeof(ViewStub));
Assert.Null(handlerType);
}
+
+ [Fact]
+ public void SecondConfigureMauiHandlersCallReplacesHandler()
+ {
+ var mauiApp = MauiApp.CreateBuilder()
+ .ConfigureMauiHandlers(handlers => handlers.AddHandler())
+ .ConfigureMauiHandlers(handlers => handlers.AddHandler())
+ .Build();
+
+ var mauiHandlersFactory = mauiApp.Services.GetRequiredService();
+
+ var handlerService = mauiHandlersFactory.GetHandler(typeof(ViewStub));
+ Assert.NotNull(handlerService);
+ Assert.IsType(handlerService);
+ }
+
+ [Fact]
+ public void FactoryBasedHandlerRegistrationReplacesHandler()
+ {
+ var mauiApp = MauiApp.CreateBuilder()
+ .ConfigureMauiHandlers(handlers => handlers.AddHandler())
+ .ConfigureMauiHandlers(handlers => handlers.AddHandler(_ => new AlternateViewHandlerStub()))
+ .Build();
+
+ var mauiHandlersFactory = mauiApp.Services.GetRequiredService();
+
+ var handlerService = mauiHandlersFactory.GetHandler(typeof(ViewStub));
+ Assert.NotNull(handlerService);
+ Assert.IsType(handlerService);
+ }
+
+ class AlternateViewHandlerStub : ViewHandlerStub
+ {
+ }
}
}
\ No newline at end of file