Blazor Routing and Navigation

This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14.

This section’s bibliography lists the reference material consulted while preparing these pages.

Blazor routes URLs to components with the same @page mechanism regardless of render mode, and layers client-side navigation on top of it. See Blazor Overview and Render Modes for how a routed component’s render mode is chosen.

@page and route templates

@page "/products/{Id:int}"
@page "/products/{Id:int}/{Slug?}"

@code {
    [Parameter] public int Id { get; set; }
    [Parameter] public string? Slug { get; set; }
}

Constraints (:int, :guid, :bool, …​), optional segments (\{Slug?}), and catch-alls (\{*rest}) work the same as MVC route templates — see Routing for the full constraint list. A component can declare more than one @page directive to be reachable at multiple URLs.

Router, AppAssembly, and AdditionalAssemblies

Routes.razor hosts the Router component, which scans assemblies for @page-annotated components at startup:

<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Shared.Nav).Assembly }">
    <Found Context="routeData">
        <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

AppAssembly is scanned by default; AdditionalAssemblies adds routable components shipped in a referenced Razor class library (see Razor class libraries).

NavigationManager

@inject NavigationManager Nav
@implements IDisposable

<button @onclick='() => Nav.NavigateTo("/products")'>Back</button>
<button @onclick='() => Nav.NavigateTo(Nav.GetUriWithQueryParameter("page", 2))'>Next page</button>

@code {
    protected override void OnInitialized() => Nav.LocationChanged += OnLocationChanged;

    private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
        => Console.WriteLine($"Navigated to {e.Location}");

    public void Dispose() => Nav.LocationChanged -= OnLocationChanged;
}

NavigateTo(uri, forceLoad: true) bypasses client-side routing for a full page reload; NavigateTo(uri, replace: true) replaces the current history entry instead of pushing a new one. GetUriWithQueryParameter builds a new URL with one query parameter added/changed/removed, which is the supported way to update the query string without hand-building strings.

[SupplyParameterFromQuery]

Bind a component parameter straight from a query-string value instead of reading NavigationManager.Uri manually:

@page "/products"

@code {
    [SupplyParameterFromQuery] public string? Search { get; set; }
    [SupplyParameterFromQuery(Name = "p")] public int Page { get; set; } = 1;
}
<NavLink href="/products" Match="NavLinkMatch.All">All products</NavLink>
<NavLink href="/products" Match="NavLinkMatch.Prefix">Products section</NavLink>

NavLink renders an <a> and adds the active CSS class when the current URL matches — Match.All requires an exact match, Match.Prefix (the default) matches any URL that starts with href.

Navigation locks

NavigationLock intercepts an in-app navigation attempt (and, in supporting browsers, a tab close) to confirm unsaved changes before leaving:

<NavigationLock OnBeforeInternalNavigation="ConfirmLeave" ConfirmExternalNavigation="true" />

@code {
    private void ConfirmLeave(LocationChangingContext ctx)
    {
        if (hasUnsavedChanges) ctx.PreventNavigation();
    }
}

NotFoundPage and NavigationManager.NotFound()

NET 10 lets a component declare it has no result for the current request, and the framework renders a

dedicated not-found page instead of the component’s own markup:

@page "/products/{Id:int}"
@inject NavigationManager Nav

@code {
    [Parameter] public int Id { get; set; }
    private ProductDto? product;

    protected override async Task OnParametersSetAsync()
    {
        product = await Products.FindAsync(Id);
        if (product is null) Nav.NotFound();
    }
}
@* Routes.razor *@
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
    ...
</Router>

This replaces routing to a hand-rolled "not found" component via <NotFound> for the common case of "the route matched, but the requested entity does not exist." See ASP.NET Core Blazor routing and navigation.

Enhanced navigation and form handling

As covered in the overview page, enhanced navigation intercepts same-origin link clicks and form posts with JavaScript and patches the DOM instead of a full reload — it applies across render modes, including Static SSR, and is what makes SSR form posts feel like an SPA without any client-side interactivity. Disable it per link/form with data-enhance-nav="false" when a specific navigation genuinely needs a full page load.

Layouts and nested layouts

@* MainLayout.razor *@
@inherits LayoutComponentBase
<div class="page">
    <nav>...</nav>
    <main>@Body</main>
</div>
@page "/admin/users"
@layout AdminLayout      @* nests inside whatever layout AdminLayout itself declares *@

A layout is a component implementing LayoutComponentBase, rendering @Body where the routed component goes; @layout on a page (or DefaultLayout on RouteView) selects it, and layouts can nest by one layout’s @Body containing another RouteView-rendered layout. See ASP.NET Core Blazor layouts.