ComponentRouting.Maui
2.0.1
See the version list below for details.
dotnet add package ComponentRouting.Maui --version 2.0.1
NuGet\Install-Package ComponentRouting.Maui -Version 2.0.1
<PackageReference Include="ComponentRouting.Maui" Version="2.0.1" />
<PackageVersion Include="ComponentRouting.Maui" Version="2.0.1" />
<PackageReference Include="ComponentRouting.Maui" />
paket add ComponentRouting.Maui --version 2.0.1
#r "nuget: ComponentRouting.Maui, 2.0.1"
#:package ComponentRouting.Maui@2.0.1
#addin nuget:?package=ComponentRouting.Maui&version=2.0.1
#tool nuget:?package=ComponentRouting.Maui&version=2.0.1
ComponentRouting.Maui
ComponentRouting.Maui is a .NET MAUI library for building UI flows around Component, Presenter, and Router types. It is inspired by component-based routing patterns: a component prepares state, owns or binds a presenter, and completes with a typed result when the routed UI finishes.
The library focuses on routing and composition rather than visual styling. Your MAUI pages, layouts, popups, snackbars, tabs, and root pages remain normal MAUI views that implement the marker Presenter interface.
Features
- Typed routing through
Router.PresentComponent<TComponent, TState, TResult>(state). - Component preloading through
Router.PreloadComponent<TComponent, TState, TResult>(state). - Push and modal dismissal through
Router.DismissComponent<TComponent, TState, TResult>(animated). - Mounted overlay and snackbar lookup through
GetMountedComponent<TComponent>()andGetMountedComponents<TComponent>(). - Extensible routing through
AbstractRouter, includingRootComponentandCanNavigateBack(...). - Component creation through
ComponentFactory.CreateComponent<T>(). - Microsoft dependency injection registration and component scanning with
AddComponentRoutingMaui(...). - Built-in component base types for root, page, modal, push, overlay, snackbar, tab, and flyout flows.
- Singleton registration for normal components and transient registration for overlays and snackbars.
Requirements
- .NET 10 SDK.
- .NET MAUI workload for MAUI apps and the sample app.
Microsoft.Extensions.DependencyInjection.NGettext, because the public localization interfaces exposeNGettext.ICatalog.
The package targets:
net10.0-androidnet10.0-iosnet10.0-maccatalyst
Starting from version 2.0.0, ComponentRouting.Maui is packaged only for MAUI platform target frameworks. Plain net10.0 consumers are no longer supported. Existing MAUI consumers should remain source-compatible, but projects must target a supported MAUI platform TFM.
Installation
Install the package from NuGet:
dotnet add package ComponentRouting.Maui --version 2.0.1
Basic Setup
Register ComponentRouting.Maui in MauiProgram.cs and include the assembly that contains your components and router:
using ComponentRouting.Maui;
using ComponentRouting.Maui.Ioc;
using ComponentRouting.Maui.Provider.Core;
using ComponentRouting.Maui.Sample.Routing;
using ComponentRouting.Maui.Sample.Services;
using ComponentRouting.Maui.Service.Core;
builder.Services.AddComponentRoutingMaui(typeof(SampleRouter).Assembly);
builder.Services.AddSingleton<SampleRouter>();
builder.Services.AddSingleton<Router>(sp => sp.GetRequiredService<SampleRouter>());
builder.Services.AddSingleton<CatalogProvider, SampleCatalogProvider>();
builder.Services.AddSingleton<SafeAreaInsetsService, SampleSafeAreaInsetsService>();
AddComponentRoutingMaui(...) scans exported types from the provided assemblies, registers concrete components, registers ComponentFactory, and registers the first discovered concrete CatalogProvider, LocaleProvider, and Router when available. The sample registers its router and required services explicitly.
Router
Create a router by deriving from AbstractRouter:
using ComponentRouting.Maui;
using ComponentRouting.Maui.Abstraction;
using ComponentRouting.Maui.Provider.Core;
using ComponentRouting.Maui.Routing;
using ComponentRouting.Maui.Sample.Components.Root;
using ComponentRouting.Maui.Service.Core;
public sealed class SampleRouter : AbstractRouter
{
public SampleRouter(
ComponentFactory componentFactory,
CatalogProvider catalogProvider,
SafeAreaInsetsService safeAreaInsetsService)
: base(componentFactory, catalogProvider, safeAreaInsetsService)
{
}
public override RootComponent RootComponent =>
ComponentFactory.CreateComponent<SampleTabbedRootComponent>();
protected override bool CanNavigateBack(Component component)
{
return true;
}
}
The router creates components through ComponentFactory, applies localization for LocalizableComponent, presents the component on the MAUI main thread, and resolves the component's typed result.
Minimal Page Component
A page flow pairs a component with a MAUI page that implements Presenter:
using ComponentRouting.Maui;
using ComponentRouting.Maui.Abstraction;
using Microsoft.Maui.Controls;
public sealed class LoginComponent
: PageComponent<LoginComponent.ComponentState, LoginResult>
{
public readonly record struct ComponentState(string Title, string Message);
protected override Presenter CreatePresenter()
{
return new LoginPage();
}
protected override Task Configure(ComponentState state)
{
return Task.CompletedTask;
}
protected override Task Initialize(ComponentState state)
{
((LoginPage)Presenter!).Initialize(
state.Title,
state.Message,
() => CompletionSource?.TrySetResult(LoginResult.SignedIn),
() => CompletionSource?.TrySetResult(LoginResult.Cancelled));
return Task.CompletedTask;
}
protected override Task PresentInternal()
{
return Task.CompletedTask;
}
}
public enum LoginResult
{
SignedIn,
Cancelled
}
public partial class LoginPage : ContentPage, Presenter
{
private Action? complete;
private Action? cancel;
public void Initialize(string title, string message, Action complete, Action cancel)
{
Title = title;
this.complete = complete;
this.cancel = cancel;
}
}
Open the component through the injected Router:
var result = await router.PresentComponent<LoginComponent, LoginComponent.ComponentState, LoginResult>(
new LoginComponent.ComponentState("Login page", "Complete this page to return a result."));
The generic arguments keep the component state and result explicit at the call site.
Component Types
RootComponentmounts the app's root presenter.PageComponent<TState, TResult>replaces the current window page.ModalPageComponent<TState, TResult>presents a modal MAUI page.PushableComponent<TState, TResult>pushes a page onto the current navigation stack.OverlayComponent<TState, TResult>mounts a MAUI layout over anOverlayHost.SnackbarComponentis an overlay specialized forSnackbarConfiguration.TabComponent<TState>represents a tab-bound component.FlyoutComponent<TState>is supported by the router as a flyout-bound component type.
Overlays And Snackbars
Overlays and snackbars are registered as transient components, so each presentation gets a separate instance.
_ = router.PresentComponent<LoadingPopupComponent, LoadingPopupComponent.ComponentState, bool>(
new LoadingPopupComponent.ComponentState("Loading popup", "Mounted lookup can hide this overlay."));
router.GetMountedComponent<LoadingPopupComponent>()?.Unpresent();
Snackbars use SnackbarConfiguration:
_ = router.PresentComponent<InfoSnackbarComponent, SnackbarConfiguration, bool>(
new SnackbarConfiguration("Saved", mustCloseAutomatically: true, closureDelayMs: 3000));
Mounted lookup searches the router's overlay and snackbar history:
var popup = router.GetMountedComponent<LoadingPopupComponent>();
var snackbars = router.GetMountedComponents<InfoSnackbarComponent>();
GetMountedComponent<TComponent>() returns null when no mounted instance exists, returns the single match when exactly one is mounted, and throws InvalidOperationException when multiple matching instances exist. Use GetMountedComponents<TComponent>() when multiple overlays or snackbars can be present.
Tabs And Flyout
The sample app shows tab routing with a RootComponent that creates a tabbed root page and binds existing tab presenters to TabComponent<TState> instances through ComponentFactory.CreateComponent<T>().
FlyoutComponent<TState> is also a supported router component type. This repository does not currently include a flyout sample.
Sample App
The sample app is in SAMPLE/ComponentRouting.Maui.Sample.
It demonstrates:
- root and tabbed navigation through
SampleTabbedRootComponent; - page routing with
LoginComponent; - modal routing with
DetailsComponent; - pushable wizard flow with
WizardStepComponentandWizardConfirmComponent; - overlay presentation with
LoadingPopupComponent; - snackbar presentation with
InfoSnackbarComponent; - mounted overlay and snackbar lookup;
- DI registration through
AddComponentRoutingMaui(...),SampleRouter,CatalogProvider, andSafeAreaInsetsService.
The app creates an initial placeholder window page, then presents the root component when the window is created.
Build And Test
dotnet restore ComponentRouting.Maui.sln
dotnet build ComponentRouting.Maui/ComponentRouting.Maui.csproj -f net10.0-android -c Release --no-restore
dotnet test ComponentRouting.Maui.Tests/ComponentRouting.Maui.Tests.csproj -c Release --no-restore
dotnet build SAMPLE/ComponentRouting.Maui.Sample/ComponentRouting.Maui.Sample.csproj -f net10.0-android -c Debug
The Android sample build requires the .NET MAUI workload and Android platform tooling.
Changelog
See CHANGELOG.md for release notes and compatibility changes.
Current Limitations
- Dependency injection integration is based on
Microsoft.Extensions.DependencyInjection. - Mounted component lookup is intentionally scoped to overlays and snackbars, not root, page, tab, flyout, modal, or push stack components.
Local development with ProjectReference
This library is intended to be consumed primarily as a NuGet package.
For local development, debugging, or testing changes before publishing a new package, you can also clone the repository and reference the project directly with a ProjectReference from a consuming .NET MAUI app.
This mode is optional and should be treated as a development-only workflow. Normal consumers should use the NuGet package.
Enable ProjectReference mode locally
To enable local ProjectReference mode, create a file named:
Directory.Build.local.props
in the same directory as:
Directory.Build.props
Do not commit this file. It is meant to contain local machine/developer settings only.
Recommended local configuration:
<Project>
<PropertyGroup>
<UseAsProjectReference>true</UseAsProjectReference>
<OverrideAndroidSpecificVersion>36.0</OverrideAndroidSpecificVersion>
</PropertyGroup>
</Project>
What this does
By default, the project uses package-oriented, generic .NET MAUI platform TFMs, for example:
net10.0-ios
net10.0-android
When UseAsProjectReference is enabled, the project can adjust its target frameworks to match the platform-specific target required by a consuming app.
For example, with:
<OverrideAndroidSpecificVersion>36.0</OverrideAndroidSpecificVersion>
the Android target becomes:
net10.0-android36.0
This is useful when a consuming app targets a specific Android platform version and the library is referenced directly as a project instead of as a NuGet package.
If UseAsProjectReference=true is set and OverrideAndroidSpecificVersion is not provided, the project is configured to fall back to Android 36.0 for ProjectReference mode. Setting the value explicitly is still recommended because it makes the consuming setup easier to read.
Optional iOS override
If a consuming app requires a specific iOS platform version, use:
<OverrideIosSpecificVersion>26.0</OverrideIosSpecificVersion>
In that case, the iOS target becomes:
net10.0-ios26.0
If OverrideIosSpecificVersion is not set, the iOS target remains generic:
net10.0-ios
Optional MacCatalyst override
Projects that include MacCatalyst also support:
<OverrideMacCatalystSpecificVersion>26.0</OverrideMacCatalystSpecificVersion>
When set together with UseAsProjectReference=true, this changes the MacCatalyst target to:
net10.0-maccatalyst26.0
Important notes
Directory.Build.local.propsis for local development only.- Do not commit
Directory.Build.local.props. - Normal NuGet builds and CI builds should run without this local file.
- When the local file is not present,
UseAsProjectReferencedefaults tofalse. - When
UseAsProjectReferenceisfalse, the project uses its normal package-oriented target frameworks. - If the project supports MacCatalyst,
OverrideMacCatalystSpecificVersioncan be used in the same way as the Android and iOS overrides. - If you switch between package mode and project-reference mode, clean
binandobjfolders before rebuilding. - Restore and build should be performed in the same mode. If restore runs with local overrides enabled, build should use the same overrides.
- If Rider keeps building against an old Android/iOS target after switching modes, reload all projects. If the problem persists, use File > Invalidate Caches... and reopen the solution.
Packing and testing the NuGet package locally
When creating or testing the NuGet package, make sure the local ProjectReference overrides are disabled. Otherwise the package can be produced with development-specific target frameworks.
Before packing, temporarily rename the local props file if it exists:
mv Directory.Build.local.props Directory.Build.local.props.disabled
Then clean generated folders from the repository root:
find . -type d \( -name bin -o -name obj \) -prune -exec rm -rf {} +
Pack the library by calling dotnet pack directly on the library .csproj, not on the solution root:
dotnet pack ComponentRouting.Maui/ComponentRouting.Maui.csproj \
-c Release \
-o ./local-nuget
Packing the concrete library project avoids unintentionally building sample apps, tests, or other projects in the solution.
After packing, you can re-enable your local development settings:
mv Directory.Build.local.props.disabled Directory.Build.local.props
To inspect the generated package contents:
unzip -l ./local-nuget/ComponentRouting.Maui.2.0.1.nupkg | grep "lib/"
With .NET MAUI/.NET 10, it is normal for the generated .nupkg to contain platform-normalized asset folders such as:
lib/net10.0-android36.0/
lib/net10.0-ios26.0/
even when the project file declares generic TFMs such as net10.0-android or net10.0-ios. Those platform versions are resolved by the installed .NET SDK/workloads during build/pack.
To test the package without publishing it, add ./local-nuget as a local NuGet source in a consuming app and use the normal PackageReference workflow. This is the best way to verify the package as a real consumer would use it.
License
ComponentRouting.Maui is released under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-android36.0 is compatible. net10.0-ios26.0 is compatible. net10.0-maccatalyst26.0 is compatible. |
-
net10.0-android36.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.8)
- Microsoft.Maui.Controls (>= 10.0.20)
- NGettext (>= 0.6.7)
-
net10.0-ios26.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.8)
- Microsoft.Maui.Controls (>= 10.0.20)
- NGettext (>= 0.6.7)
-
net10.0-maccatalyst26.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.8)
- Microsoft.Maui.Controls (>= 10.0.20)
- NGettext (>= 0.6.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.3.1 | 129 | 7/9/2026 |
| 5.3.0 | 119 | 7/2/2026 |
| 5.2.2 | 118 | 7/2/2026 |
| 5.2.1 | 116 | 7/1/2026 |
| 5.2.0 | 121 | 7/1/2026 |
| 5.1.0 | 123 | 7/1/2026 |
| 5.0.2 | 120 | 7/1/2026 |
| 5.0.1 | 120 | 6/30/2026 |
| 5.0.0 | 119 | 6/27/2026 |
| 4.0.2 | 130 | 6/25/2026 |
| 4.0.1 | 124 | 6/22/2026 |
| 4.0.0 | 127 | 6/22/2026 |
| 3.0.0 | 224 | 6/17/2026 |
| 2.0.1 | 124 | 6/12/2026 |
| 2.0.0 | 125 | 6/10/2026 |
| 1.0.0 | 123 | 6/1/2026 |