> ## Content Index
> Fetch the complete content index at: https://cronoxyd.de/llms.txt
> Use this file to discover other available public pages before exploring further.

# .NET MAUI Blazor windows
- URL: https://cronoxyd.de/net-maui-blazor-windows/
- Published: 2023-05-21T19:08:48.000Z
- Updated: 2026-08-01T15:02:32.000Z
- Author: Marcel Diskowski
- Tags: Coding

# .NET MAUI Blazor windows

The .NET MAUI documentation details the creation of windows and states that .NET MAUI apps support having multiple windows, but what it omits is explaining how to access this functionality in the context of a .NET MAUI Blazor app.

Fundamentally, a .NET MAUI Blazor app is just a normal MAUI app with a single page (`MainPage.xaml`) that has a [BlazorWebView](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.components.webview.maui.blazorwebview?view=net-maui-7.0&ref=cronoxyd.de) which then actually hosts the Razor content. That means that any newly created window will still have the same `MainPage.xaml` as its content and it is up to the Blazor-side to present the correct content.

## .NET MAUI app startup

First, a little background about how a .NET MAUI Blazor application starts (in the boilerplate project):

`Program.cs`

The entry point is `CreateMauiApp()` which references `App` by using it as a type argument in the generic method `UseMauiApp()`.

`App.xaml(.cs)` (derives from `Microsoft.Maui.Controls.Application`)

Sets its `MainPage` property (inherited from its base class) to a new instance of `MainPage`. According to the [.NET MAUI documentation](https://learn.microsoft.com/dotnet/maui/fundamentals/windows?ref=cronoxyd.de#create-a-window?view=net-maui-7.0) on windows, setting the `MainPage` property of an `Application` will cause a `Window` to be created.

`MainPage.xaml(.cs)`

Contains a [BlazorWebView](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.components.webview.maui.blazorwebview?view=net-maui-7.0&ref=cronoxyd.de) that references the static `wwwroot/index.html` as the HTML boilerplate/skeleton and adds the `Main` type as the only `RootComponent` of the `BlazorWebView`.

`Main.razor(.cs)`

Contains a `Router` component that, according to the [Blazor routing and navigation documentation](https://learn.microsoft.com/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-7.0&ref=cronoxyd.de), scans the specified `AppAssembly` to "gather route information for the app's components that have a RouteAttribute".

So we need to provide a way for Razor components to create a new MAUI window with the possibility to specify an URI that references a specific page within the `AppAssembly` of the `Router`.

## Implementation

The following details the implementation from the bottom up in terms of the startup described above:

### `Main.razor.cs` (create)

Firstly, we need to provide a way to specify a page other that the index (i.e. the page that has the `/` route) in the `Main` razor component. So, we add a parameter to specify the URI of the page we want to view and navigate to that URI right after initializing the component:

```diff
+public partial class Main : ComponentBase
+{
+    [Parameter]
+    public string PageUri { get; set; } = string.Empty;
+
+    protected override void OnInitialized()
+    {
+        if (!string.IsNullOrEmpty(PageUri))
+            Nav.NavigateTo(PageUri);
+    }
+}

```

### `MainPage.xaml`

Remove the `<BlazorWebView.RootComponents>` element from the XAML completly. We will populate this collection in the code-behind instead:

```diff
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:RecEx.App"
             x:Class="RecEx.App.MainPage"
             BackgroundColor="{DynamicResource PageBackgroundColor}">

    <BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
-        <BlazorWebView.RootComponents>
-            <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" Parameters="{Binding Path=RootComponentParameters}" />
-        </BlazorWebView.RootComponents>
    </BlazorWebView>

</ContentPage>

```

### `MainPage.xaml.cs` (create)

The items in the `RootComponents` collection provide a way to pass parameters to the specified component in the form of a `IDictionary<string, object>`. So we add an optional argument to the constructor to expose this to callers:

```diff
+public partial class MainPage : ContentPage
+{
+    public MainPage(IDictionary<string, object> rootComponentParameters = null)
+    {
+        InitializeComponent();
+
+        blazorWebView.RootComponents.Add(new()
+        {
+            ComponentType = typeof(Main),
+            Parameters = rootComponentParameters,
+            Selector = "#app"
+        });
+    }
+}

```

### `App.xaml.cs`

Finally, we add a static class that provides extension methods to open the window:

```diff
public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        MainPage = new MainPage();
    }
}

+public static class ApplicationExtensions
+{
+    public static Window OpenWindowFromPage(this Application app, Page page)
+    {
+        var windowObj = new Window(page);
+        windowObj.Parent = app.Windows[0];
+        app.OpenWindow(windowObj);
+        return windowObj;
+    }
+    public static void OpenInNewWindow(this Application app, string pageUri)
+    {
+        var windowPage = new MainPage(new Dictionary<string, object>()
+        {
+            {nameof(Main.PageUri), pageUri }
+        });
+        app.OpenWindowFromPage(windowPage);
+    }
+
+    public static void OpenInNewWindow(this ComponentBase component, string pageUri) =>
+        Application.Current.OpenInNewWindow(pageUri);
+}

```

## Usage

To open a Razor page in a new MAUI window, simply call the `OpenInNewWindow` extension method from any component:

```razor
@page "/"

<h1>.NET MAUI Blazor window demo</h1>
<button type="button" @onclick="() => OpenCounterWindow()">Open counter window</button>

@code {
    protected void OpenCounterWindow()
    {
        this.OpenInNewWindow("/counter");
    }
}

```

## Limitations/caveats

1. There does not seem to be any support for modal windows (yet?). I've experimented with the `Parent` property on `Window`, but to no avail.
2. In my tests, I could always briefly see the content of the `Index.razor` page because the `Router` loads it by default. In addition to being a cosmetic issue, it also means that any code on the `Index` page is executed before the specified page is loaded.