Tracking issue: dotnet/maui#31945
Background
The maui-blazor-web solution template enables sharing Razor components between Blazor Web and Blazor Hybrid (MAUI) apps via a shared Razor Class Library (RCL). When using per-page/component interactivity (e.g., @rendermode InteractiveAuto on a page), the Blazor Web app works fine, but the MAUI Blazor Hybrid app throws:
NotSupportedException: Cannot supply a component of type 'Counter' because the current platform does not support the render mode 'InteractiveAuto'.
This is because WebViewRenderer does not override ResolveComponentForRenderMode, and the Renderer base class throws by default.
Problem
WebViewRenderer (source) inherits from WebRenderer but does not override ResolveComponentForRenderMode.
Renderer.ResolveComponentForRenderMode (source) throws NotSupportedException by default.
PageContext (source) creates WebViewRenderer privately — consumers (like MAUI's BlazorWebView) have no way to provide a custom renderer or override this behavior.
Why this cannot be solved in MAUI
Both WebViewRenderer and PageContext are internal sealed in this repo. WebViewManager (which MAUI subclasses) creates PageContext in its internal AttachToPageAsync method, and PageContext creates WebViewRenderer in its constructor. There is no factory pattern, DI hook, or extensibility point that would allow MAUI to customize the renderer.
Proposed Solutions
As @SteveSandersonMS suggested in dotnet/maui#31945:
Option A (minimal — unconditional override): Override ResolveComponentForRenderMode in WebViewRenderer to treat all interactive render modes as no-ops, since Blazor Hybrid is always interactive. This follows the same pattern as RemoteRenderer and WebAssemblyRenderer:
// In WebViewRenderer.cs
protected internal override IComponent ResolveComponentForRenderMode(
Type componentType,
int? parentComponentId,
IComponentActivator componentActivator,
IComponentRenderMode renderMode)
{
// Blazor Hybrid is always interactive — all render modes are no-ops
return componentActivator.CreateInstance(componentType);
}
Option B (extensible): Add extensibility to PageContext or WebViewRenderer so that BlazorWebView consumers can configure render mode resolution behavior.
Option C (longer-term, from Steve's suggestion): Introduce a generic Interactive render mode that web apps resolve based on global configuration, while WebView treats as a no-op.
Option D (AppContext switch — allows host app to opt in): Add an AppContext switch that the Renderer base class checks before throwing in its default ResolveComponentForRenderMode. This follows the existing pattern used in the Components area (e.g., Microsoft.AspNetCore.Components.Unsupported.DisablePropertyInjection in ComponentFactory.cs and Microsoft.AspNetCore.Components.Routing.RegexConstraintSupport in RegexConstraintSupport.cs):
// In Renderer.cs — change the default fallback behavior
private static readonly bool _suppressRenderModeExceptions =
AppContext.TryGetSwitch(
"Microsoft.AspNetCore.Components.SuppressUnsupportedRenderModes",
out var suppress) && suppress;
protected internal virtual IComponent ResolveComponentForRenderMode(
Type componentType,
int? parentComponentId,
IComponentActivator componentActivator,
IComponentRenderMode renderMode)
{
if (_suppressRenderModeExceptions)
{
// When the switch is set, treat unsupported render modes as no-ops.
// This enables Blazor Hybrid and other hosts where everything is already interactive.
return componentActivator.CreateInstance(componentType);
}
throw new NotSupportedException(
$"Cannot supply a component of type '{ componentType }' because the current platform "
+ $"does not support the render mode '{renderMode}'.");
}
The host app (e.g., MAUI) would set the switch at startup:
// In MauiProgram.cs (or set automatically by MAUI's BlazorWebView handler):
AppContext.SetSwitch(
"Microsoft.AspNetCore.Components.SuppressUnsupportedRenderModes", true);
Or via MSBuild (no code needed):
<ItemGroup>
<RuntimeHostConfigurationOption
Include="Microsoft.AspNetCore.Components.SuppressUnsupportedRenderModes"
Value="true" />
</ItemGroup>
Advantages of the AppContext switch approach:
- Follows established patterns already in
Microsoft.AspNetCore.Components
- Host app controls the opt-in (MAUI could set this automatically in its BlazorWebView initialization)
- Can be set via code or via
<RuntimeHostConfigurationOption> in csproj
- Works for any renderer, not just WebView
- Minimal aspnetcore change (~5 lines in one file)
Current Workaround
Users must create a wrapper class in the shared RCL that nulls out render modes for Hybrid:
public static class InteractiveRenderSettings
{
public static IComponentRenderMode? InteractiveServer { get; set; } = RenderMode.InteractiveServer;
public static IComponentRenderMode? InteractiveAuto { get; set; } = RenderMode.InteractiveAuto;
public static IComponentRenderMode? InteractiveWebAssembly { get; set; } = RenderMode.InteractiveWebAssembly;
public static void ConfigureBlazorHybridRenderModes()
{
InteractiveServer = null;
InteractiveAuto = null;
InteractiveWebAssembly = null;
}
}
Components must use @rendermode InteractiveRenderSettings.InteractiveAuto instead of @rendermode InteractiveAuto, and the MAUI app calls ConfigureBlazorHybridRenderModes() at startup. (Sample)
Limitations of the workaround:
- Requires modifying every component that uses render modes
- Does not work for 3rd-party NuGet packages that set render modes directly
- Adds boilerplate that the framework should handle natively
Steps to Reproduce
- Create a project using
dotnet new maui-blazor-web with InteractiveAuto render mode
- In the
.Web project's App.razor, remove @rendermode="InteractiveAuto" from HeadOutlet and Routes
- In the
.Shared project's Counter.razor, add @rendermode InteractiveAuto at the top
- Run the MAUI app → throws NotSupportedException
Impact
Related Issues
Tracking issue: dotnet/maui#31945
Background
The
maui-blazor-websolution template enables sharing Razor components between Blazor Web and Blazor Hybrid (MAUI) apps via a shared Razor Class Library (RCL). When using per-page/component interactivity (e.g.,@rendermode InteractiveAutoon a page), the Blazor Web app works fine, but the MAUI Blazor Hybrid app throws:This is because
WebViewRendererdoes not overrideResolveComponentForRenderMode, and theRendererbase class throws by default.Problem
WebViewRenderer(source) inherits fromWebRendererbut does not overrideResolveComponentForRenderMode.Renderer.ResolveComponentForRenderMode(source) throwsNotSupportedExceptionby default.PageContext(source) createsWebViewRendererprivately — consumers (like MAUI'sBlazorWebView) have no way to provide a custom renderer or override this behavior.Why this cannot be solved in MAUI
Both
WebViewRendererandPageContextareinternal sealedin this repo.WebViewManager(which MAUI subclasses) createsPageContextin its internalAttachToPageAsyncmethod, andPageContextcreatesWebViewRendererin its constructor. There is no factory pattern, DI hook, or extensibility point that would allow MAUI to customize the renderer.Proposed Solutions
As @SteveSandersonMS suggested in dotnet/maui#31945:
Option A (minimal — unconditional override): Override
ResolveComponentForRenderModeinWebViewRendererto treat all interactive render modes as no-ops, since Blazor Hybrid is always interactive. This follows the same pattern asRemoteRendererandWebAssemblyRenderer:Option B (extensible): Add extensibility to
PageContextorWebViewRendererso thatBlazorWebViewconsumers can configure render mode resolution behavior.Option C (longer-term, from Steve's suggestion): Introduce a generic
Interactiverender mode that web apps resolve based on global configuration, while WebView treats as a no-op.Option D (AppContext switch — allows host app to opt in): Add an
AppContextswitch that theRendererbase class checks before throwing in its defaultResolveComponentForRenderMode. This follows the existing pattern used in the Components area (e.g.,Microsoft.AspNetCore.Components.Unsupported.DisablePropertyInjectioninComponentFactory.csandMicrosoft.AspNetCore.Components.Routing.RegexConstraintSupportinRegexConstraintSupport.cs):The host app (e.g., MAUI) would set the switch at startup:
Or via MSBuild (no code needed):
Advantages of the AppContext switch approach:
Microsoft.AspNetCore.Components<RuntimeHostConfigurationOption>in csprojCurrent Workaround
Users must create a wrapper class in the shared RCL that nulls out render modes for Hybrid:
Components must use
@rendermode InteractiveRenderSettings.InteractiveAutoinstead of@rendermode InteractiveAuto, and the MAUI app callsConfigureBlazorHybridRenderModes()at startup. (Sample)Limitations of the workaround:
Steps to Reproduce
dotnet new maui-blazor-webwith InteractiveAuto render mode.Webproject'sApp.razor, remove@rendermode="InteractiveAuto"fromHeadOutletandRoutes.Sharedproject'sCounter.razor, add@rendermode InteractiveAutoat the topImpact
maui-blazor-webtemplate (sharing interactive components between Web and Hybrid)Related Issues