Blazor Hybrid/MAUI-specific extensions for Blazouter routing library. This package provides optimized components and integration for Blazor Hybrid applications running on .NET MAUI. Enables React Router-like routing features in native mobile and desktop apps with full support for nested routes, route guards, lazy loading, smooth transitions, and programmatic navigation. Includes platform-specific optimizations for iOS, Android, macOS, and Windows.
$ dotnet add package Blazouter.HybridHybrid/MAUI-specific extensions for Blazouter - the React Router-like routing library for Blazor applications. This package provides optimized components and extensions for Blazor Hybrid applications running on .NET MAUI.
dotnet add package Blazouter
dotnet add package Blazouter.Hybrid
Note: This package requires the core Blazouter package and is designed for .NET MAUI projects.
// MauiProgram.cs
using Blazouter.Hybrid.Extensions;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.AddBlazouterSupport() // Add Blazouter support
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
#endif
return builder.Build();
}
}
@using Blazouter.Models
@using Blazouter.Components
<Router Routes="@_routes" DefaultLayout="typeof(Layout.MainLayout)">
<NotFound>
<div class="not-found">
<h1>404 - Page Not Found</h1>
</div>
</NotFound>
</Router>
@code {
private List<RouteConfig> _routes = new()
{
new RouteConfig
{
Path = "/",
Component = typeof(Pages.Home),
Title = "Home",
Transition = RouteTransition.Fade
},
new RouteConfig
{
Path = "/settings",
Component = typeof(Pages.Settings),
Title = "Settings",
Transition = RouteTransition.Slide
}
};
}
Add to your main HTML file (typically index.html in wwwroot):
<link rel="stylesheet" href="_content/Blazouter/blazouter[.min].css" />
Blazouter.Hybrid integrates seamlessly with native platform navigation:
The Hybrid package includes optimizations for:
Configure a default layout for all routes and override per route as needed:
<Router Routes="@_routes" DefaultLayout="typeof(Layout.MainLayout)">
<NotFound><h1>404</h1></NotFound>
</Router>
@code {
private List<RouteConfig> _routes = new()
{
// Uses DefaultLayout (MainLayout)
new RouteConfig
{
Path = "/",
Component = typeof(Pages.Home)
},
// Override with different layout for specific sections
new RouteConfig
{
Path = "/onboarding",
Component = typeof(Pages.Onboarding),
Layout = typeof(Layout.OnboardingLayout)
},
// No layout for fullscreen experiences
new RouteConfig
{
Path = "/camera",
Component = typeof(Pages.Camera),
Layout = null
}
};
}
Use <RouterOutlet /> for nested routing within components:
Perfect for creating tab-based or hierarchical navigation:
new RouteConfig
{
Path = "/app",
Component = typeof(MainLayout),
Children = new List<RouteConfig>
{
new RouteConfig
{
Path = "home",
Component = typeof(HomePage)
},
new RouteConfig
{
Path = "profile",
Component = typeof(ProfilePage)
},
new RouteConfig
{
Path = "settings",
Component = typeof(SettingsPage)
}
}
}
<!-- MainLayout.razor -->
@using Blazouter.Components
<div class="main-layout">
<nav class="bottom-nav">
<RouterLink Href="/app/home" ActiveClass="active">Home</RouterLink>
<RouterLink Href="/app/profile" ActiveClass="active">Profile</RouterLink>
<RouterLink Href="/app/settings" ActiveClass="active">Settings</RouterLink>
</nav>
<div class="content">
<RouterOutlet />
</div>
</div>
Note: <RouterOutlet /> is for nested routing within a component hierarchy, while Layout (via DefaultLayout or RouteConfig.Layout) wraps entire routes with a common layout structure like headers, footers, and navigation.
Implement authentication or platform-specific checks:
new RouteConfig
{
Path = "/premium",
Component = typeof(PremiumFeatures),
Guards = new List<Type> { typeof(SubscriptionGuard) }
}
public class SubscriptionGuard : IRouteGuard
{
private readonly ISubscriptionService _subscriptionService;
public SubscriptionGuard(ISubscriptionService subscriptionService)
{
_subscriptionService = subscriptionService;
}
public async Task<bool> CanActivateAsync(RouteMatch match)
{
return await _subscriptionService.HasActiveSubscriptionAsync();
}
public Task<string?> GetRedirectPathAsync(RouteMatch match)
{
return Task.FromResult<string?>("/subscribe");
}
}
@inject RouterNavigationService NavService
private async Task OnBackButtonPressed()
{
// Handle Android back button
#if ANDROID
if (CanGoBack())
{
NavService.NavigateTo(-1);
}
else
{
// Exit app or show exit confirmation
}
#endif
}
Reduce app startup time by loading features on-demand:
new RouteConfig
{
Path = "/reports",
ComponentLoader = async () =>
{
// Load heavy component only when needed
await Task.Delay(100);
return typeof(ReportsPage);
},
Title = "Reports"
}
Smooth animations optimized for mobile devices:
// Fade - Good for subtle transitions
new RouteConfig
{
Path = "/home",
Component = typeof(HomePage),
Transition = RouteTransition.Fade
}
// Slide - Natural for hierarchical navigation
new RouteConfig
{
Path = "/details",
Component = typeof(DetailsPage),
Transition = RouteTransition.Slide
}
// SlideUp - Great for modal-like screens
new RouteConfig
{
Path = "/modal",
Component = typeof(ModalPage),
Transition = RouteTransition.SlideUp
}
@inject RouterNavigationService NavService
// Navigate to a route
private void GoToDetail(int itemId)
{
NavService.NavigateTo($"/items/{itemId}");
}
// Go back
private void GoBack()
{
NavService.NavigateTo(-1);
}
// Navigate with query parameters
private void SearchItems(string query)
{
NavService.NavigateTo($"/search?q={query}");
}
@inject RouterStateService RouterState
@code {
private string? _itemId;
private string? _searchQuery;
protected override void OnInitialized()
{
_itemId = RouterState.GetParam("id");
_searchQuery = RouterState.GetQueryParam("q");
}
}
See the MAUI sample application for a complete working example.
If you're migrating from .NET MAUI Shell navigation:
<Shell>
<ShellContent Title="Home" Route="home" ContentTemplate="{DataTemplate pages:HomePage}" />
<ShellContent Title="Settings" Route="settings" ContentTemplate="{DataTemplate pages:SettingsPage}" />
</Shell>
private List<RouteConfig> _routes = new()
{
new RouteConfig { Path = "/home", Component = typeof(HomePage), Title = "Home" },
new RouteConfig { Path = "/settings", Component = typeof(SettingsPage), Title = "Settings" }
};
MIT License - see LICENSE for details.
Made with ❤️ for the Blazor community