A powerful React Router-like routing library for Blazor applications. Blazouter brings advanced routing features to Blazor including nested routes, route guards, lazy loading, smooth transitions, and programmatic navigation. This is the core library that works with all Blazor hosting models (Web, Server, WebAssembly and Hybrid/MAUI).
$ dotnet add package BlazouterA powerful React Router-like routing library for Blazor applications. This is the core library that provides the foundational routing capabilities for all Blazor hosting models.
dotnet add package Blazouter
For hosting-specific implementations, also install:
Blazouter.ServerBlazouter.HybridBlazouter.WebAssembly// Program.cs
using Blazouter.Extensions;
builder.Services.AddBlazouter();
<!-- App.razor -->
@using Blazouter.Models
@using Blazouter.Components
<Router Routes="@_routes" DefaultLayout="typeof(MainLayout)">
<NotFound>
<h1>404 - Page Not Found</h1>
</NotFound>
</Router>
@code {
private List<RouteConfig> _routes = new()
{
new RouteConfig
{
Path = "/",
Component = typeof(Home),
Transition = RouteTransition.Fade
}
};
}
// Program.cs
using Blazouter.Extensions;
using Blazouter.Server.Extensions;
builder.Services.AddBlazouter();
app.MapRazorComponents<App>()
.AddBlazouterSupport()
.AddInteractiveServerRenderMode();
// MauiProgram.cs
using Blazouter.Hybrid.Extensions;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
builder.Services.AddMauiBlazorWebView();
// Add Blazouter support
builder.AddBlazouterSupport();
return builder.Build();
}
}
<!-- Main.razor or your root component -->
@using Blazouter.Models
@using Blazouter.Components
<Router Routes="@_routes">
<NotFound>
<h1>404 - Page Not Found</h1>
</NotFound>
</Router>
@code {
private List<RouteConfig> _routes = new()
{
new RouteConfig
{
Path = "/",
Component = typeof(Home),
Transition = RouteTransition.Fade
}
};
}
The main router component that handles route matching and rendering.
Used in parent components to render child routes in nested routing scenarios.
Navigation links with automatic active state detection.
<RouterLink Href="/" Exact="true" ActiveClass="active">Home</RouterLink>
<RouterLink Href="/about" ActiveClass="active">About</RouterLink>
Set a default layout for all routes and override per route:
<Router Routes="@_routes" DefaultLayout="typeof(MainLayout)">
<NotFound><h1>404</h1></NotFound>
</Router>
@code {
private List<RouteConfig> _routes = new()
{
// Uses DefaultLayout (MainLayout)
new RouteConfig { Path = "/", Component = typeof(Home) },
// Override with different layout
new RouteConfig
{
Path = "/admin",
Component = typeof(Admin),
Layout = typeof(AdminLayout)
},
// No layout for this route
new RouteConfig
{
Path = "/print",
Component = typeof(Print),
Layout = null
}
};
}
new RouteConfig
{
Path = "/users",
Component = typeof(UserLayout),
Title = "Users",
Layout = typeof(MainLayout), // Optional: override default layout
Transition = RouteTransition.Slide,
Guards = new List<Type> { typeof(AuthGuard) },
Children = new List<RouteConfig>
{
new RouteConfig
{
Path = ":id",
Component = typeof(UserDetail)
}
}
}
Create custom guards by implementing IRouteGuard:
using Blazouter.Models;
using Blazouter.Interfaces;
public class AuthGuard : IRouteGuard
{
public async Task<bool> CanActivateAsync(RouteMatch match)
{
return await IsAuthenticated();
}
public Task<string?> GetRedirectPathAsync(RouteMatch match)
{
return Task.FromResult<string?>("/login");
}
}
new RouteConfig
{
Path = "/reports",
ComponentLoader = async () =>
{
await Task.Delay(100); // Simulated delay
return typeof(ReportsPage);
}
}
@inject RouterNavigationService NavService
private void NavigateToUser()
{
NavService.NavigateTo("/users/123");
}
@inject RouterStateService RouterState
protected override void OnInitialized()
{
var userId = RouterState.GetParam("id");
}
Type-safe query string manipulation with fluent API:
@using Blazouter.Utilities
@using Blazouter.Extensions
@inject RouterStateService RouterState
@inject RouterNavigationService NavService
// Typed query parameter parsing
protected override void OnInitialized()
{
int page = RouterState.GetQueryInt("page", 1);
bool active = RouterState.GetQueryBool("active", false);
DateTime? date = RouterState.GetQueryDateTimeOrNull("date");
}
// Fluent query string building
private void SearchWithFilters()
{
NavService.NavigateToWithQuery("/search", q => q
.Add("term", "blazor")
.Add("active", true)
.Add("page", 2));
}
// Update query parameters
private void NextPage()
{
NavService.NavigateToWithUpdatedQuery(RouterState, null, q => q
.Set("page", currentPage + 1));
}
Supported types: string, int, long, decimal, double, bool, DateTime, Guid, enum, and nullable variants (15 type-safe methods each for Add() and Set()).
Enhanced browser integration with type-safe JavaScript interop using 5 specialized services:
// Enable JavaScript interop (optional)
builder.Services.AddBlazouterInterop();
Add to your index.html:
<script type="module" src="_content/Blazouter/js/index.js"></script>
Available Services:
@using Blazouter.Interops
@inject StorageInterop Storage
@inject DocumentInterop Document
@inject ViewportInterop Viewport
@inject ClipboardInterop Clipboard
@inject NavigationInterop Navigation
// Navigation
await Navigation.GoBackAsync();
string url = await Navigation.GetCurrentUrlAsync();
// Document
await Document.SetTitleAsync("Home - My App");
await Document.ScrollToTopAsync();
// Storage
await Storage.SetLocalStorageAsync("key", myObject);
var data = await Storage.GetLocalStorageAsync<MyType>("key");
// Viewport
string device = await Viewport.GetDeviceTypeAsync();
bool isMobile = await Viewport.IsMobileAsync();
// Clipboard
await Clipboard.CopyTextAsync("text");
Built-in transitions: None, Pop, Blur, Fade, Flip, Lift, Scale, Slide, Swipe, Reveal, Rotate, Curtain, SlideUp, SlideFade, Spotlight
new RouteConfig
{
Path = "/",
Component = typeof(Home),
Transition = RouteTransition.Fade
}
MIT License - see LICENSE for details.
Made with ❤️ for the Blazor community