ASP.NET Core Blazor routing

By Luke Latham

Learn how to route requests and how to use the NavLink component to create navigation links in Blazor apps.

ASP.NET Core endpoint routing integration

Blazor Server is integrated into ASP.NET Core Endpoint Routing. An ASP.NET Core app is configured to accept incoming connections for interactive components with MapBlazorHub in Startup.Configure:

app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapBlazorHub();
    endpoints.MapFallbackToPage("/_Host");
});

The most typical configuration is to route all requests to a Razor page, which acts as the host for the server-side part of the Blazor Server app. By convention, the host page is usually named _Host.cshtml. The route specified in the host file is called a fallback route because it operates with a low priority in route matching. The fallback route is considered when other routes don't match. This allows the app to use others controllers and pages without interfering with the Blazor Server app.

For information on configuring MapFallbackToPage for non-root URL server hosting, see Host and deploy ASP.NET Core Blazor.

Route templates

The Router component enables routing to each component with a specified route. The Router component appears in the App.razor file:

<Router AppAssembly="@typeof(Startup).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <p>Sorry, there's nothing at this address.</p>
    </NotFound>
</Router>

When a .razor file with an @page directive is compiled, the generated class is provided a RouteAttribute specifying the route template.

At runtime, the RouteView component:

  • Receives the RouteData from the Router along with any desired parameters.
  • Renders the specified component with its layout (or an optional default layout) using the specified parameters.

You can optionally specify a DefaultLayout parameter with a layout class to use for components that don't specify a layout. The default Blazor templates specify the MainLayout component. MainLayout.razor is in the template project's Shared folder. For more information on layouts, see ASP.NET Core Blazor layouts.

Multiple route templates can be applied to a component. The following component responds to requests for /BlazorRoute and /DifferentBlazorRoute:

@page "/BlazorRoute"
@page "/DifferentBlazorRoute"

<h1>Blazor routing</h1>

Important

For URLs to resolve correctly, the app must include a <base> tag in its wwwroot/index.html file (Blazor WebAssembly) or Pages/_Host.cshtml file (Blazor Server) with the app base path specified in the href attribute (<base href="/">). For more information, see Host and deploy ASP.NET Core Blazor.

Provide custom content when content isn't found

The Router component allows the app to specify custom content if content isn't found for the requested route.

In the App.razor file, set custom content in the NotFound template parameter of the Router component:

<Router AppAssembly="typeof(Startup).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <h1>Sorry</h1>
        <p>Sorry, there's nothing at this address.</p> b
    </NotFound>
</Router>

The content of <NotFound> tags can include arbitrary items, such as other interactive components. To apply a default layout to NotFound content, see ASP.NET Core Blazor layouts.

Route to components from multiple assemblies

Use the AdditionalAssemblies parameter to specify additional assemblies for the Router component to consider when searching for routable components. Specified assemblies are considered in addition to the AppAssembly-specified assembly. In the following example, Component1 is a routable component defined in a referenced class library. The following AdditionalAssemblies example results in routing support for Component1:

<Router
    AppAssembly="@typeof(Program).Assembly"
    AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
    ...
</Router>

Route parameters

The router uses route parameters to populate the corresponding component parameters with the same name (case insensitive):

@page "/RouteParameter"
@page "/RouteParameter/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Optional parameters aren't supported. Two @page directives are applied in the previous example. The first permits navigation to the component without a parameter. The second @page directive takes the {text} route parameter and assigns the value to the Text property.

Route constraints

A route constraint enforces type matching on a route segment to a component.

In the following example, the route to the Users component only matches if:

  • An Id route segment is present on the request URL.
  • The Id segment is an integer (int).
@page "/Users/{Id:int}"

<h1>The user Id is @Id!</h1>

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

The route constraints shown in the following table are available. For the route constraints that match with the invariant culture, see the warning below the table for more information.

Constraint Example Example Matches Invariant
culture
matching
bool {active:bool} true, FALSE No
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Yes
decimal {price:decimal} 49.99, -1,000.01 Yes
double {weight:double} 1.234, -1,001.01e8 Yes
float {weight:float} 1.234, -1,001.01e8 Yes
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638, {CD2C1638-1638-72D5-1638-DEADBEEF1638} No
int {id:int} 123456789, -123456789 Yes
long {ticks:long} 123456789, -123456789 Yes

Warning

Route constraints that verify the URL and are converted to a CLR type (such as int or DateTime) always use the invariant culture. These constraints assume that the URL is non-localizable.

Routing with URLs that contain dots

For hosted Blazor WebAssembly and Blazor Server apps, the server-side default route template assumes that if the last segment of a request URL contains a dot (.) that a file is requested (for example, https://localhost.com:5001/example/some.thing). Without additional configuration, an app returns a 404 - Not Found response if this was meant to route to a component. To use a route with one or more parameters that contains a dot, the app must configure the route with a custom template.

Consider the following Example component that can receive a route parameter from the last segment of the URL:

@page "/example"
@page "/example/{param}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string Param { get; set; }
}

To permit the Server app of a hosted Blazor WebAssembly solution to route the request with a dot in the param parameter, add a fallback file route template with the optional parameter in Startup.Configure (Startup.cs):

endpoints.MapFallbackToFile("/example/{param?}", "index.html");

To configure a Blazor Server app to route the request with a dot in the param parameter, add a fallback page route template with the optional parameter in Startup.Configure (Startup.cs):

endpoints.MapFallbackToPage("/example/{param?}", "/_Host");

For more information, see Routing in ASP.NET Core.

Catch-all route parameters

This section applies to ASP.NET Core in .NET 5 Release Candidate 1 (RC1) or later.

Catch-all route parameters, which capture paths across multiple folder boundaries, are supported in components. The catch-all route parameter must be:

  • Named to match the route segment name. Naming isn't case sensitive.
  • A string type. The framework doesn't provide automatic casting.
  • At the end of the URL.
@page "/page/{*pageRoute}"

@code {
    [Parameter]
    public string PageRoute { get; set; }
}

For the URL /page/this/is/a/test with a route template of /page/{*pageRoute}, the value of PageRoute is set to this/is/a/test.

Slashes and segments of the captured path are decoded. For a route template of /page/{*pageRoute}, the URL /page/this/is/a%2Ftest%2A yields this/is/a/test*.

Catch-all route parameters are supported in ASP.NET Core in .NET 5 Release Candidate 1 (RC1) or later.*

Use a NavLink component in place of HTML hyperlink elements (<a>) when creating navigation links. A NavLink component behaves like an <a> element, except it toggles an active CSS class based on whether its href matches the current URL. The active class helps a user understand which page is the active page among the navigation links displayed. Optionally, assign a CSS class name to NavLink.ActiveClass to apply a custom CSS class to the rendered link when the current route matches the href.

The following NavMenu component creates a Bootstrap navigation bar that demonstrates how to use NavLink components:

<div class="@NavMenuCssClass" @onclick="@ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="MyComponent" Match="NavLinkMatch.Prefix">
                <span class="oi oi-plus" aria-hidden="true"></span> My Component
            </NavLink>
        </li>
    </ul>
</div>

There are two NavLinkMatch options that you can assign to the Match attribute of the <NavLink> element:

In the preceding example, the Home NavLink href="" matches the home URL and only receives the active CSS class at the app's default base path URL (for example, https://localhost:5001/). The second NavLink receives the active class when the user visits any URL with a MyComponent prefix (for example, https://localhost:5001/MyComponent and https://localhost:5001/MyComponent/AnotherSegment).

Additional NavLink component attributes are passed through to the rendered anchor tag. In the following example, the NavLink component includes the target attribute:

<NavLink href="my-page" target="_blank">My page</NavLink>

The following HTML markup is rendered:

<a href="my-page" target="_blank">My page</a>

Warning

Due to the way that Blazor renders child content, rendering NavLink components inside a for loop requires a local index variable if the incrementing loop variable is used in the NavLink (child) component's content:

@for (int c = 0; c < 10; c++)
{
    var current = c;
    <li ...>
        <NavLink ... href="@c">
            <span ...></span> @current
        </NavLink>
    </li>
}

Using an index variable in this scenario is a requirement for any child component that uses a loop variable in its child content, not just the NavLink component.

Alternatively, use a foreach loop with Enumerable.Range:

@foreach(var c in Enumerable.Range(0,10))
{
    <li ...>
        <NavLink ... href="@c">
            <span ...></span> @c
        </NavLink>
    </li>
}

URI and navigation state helpers

Use NavigationManager to work with URIs and navigation in C# code. NavigationManager provides the event and methods shown in the following table.

Member Description
Uri Gets the current absolute URI.
BaseUri Gets the base URI (with a trailing slash) that can be prepended to relative URI paths to produce an absolute URI. Typically, BaseUri corresponds to the href attribute on the document's <base> element in wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server).
NavigateTo Navigates to the specified URI. If forceLoad is true:
  • Client-side routing is bypassed.
  • The browser is forced to load the new page from the server, whether or not the URI is normally handled by the client-side router.
LocationChanged An event that fires when the navigation location has changed.
ToAbsoluteUri Converts a relative URI into an absolute URI.
ToBaseRelativePath Given a base URI (for example, a URI previously returned by BaseUri), converts an absolute URI into a URI relative to the base URI prefix.

The following component navigates to the app's Counter component when the button is selected:

@page "/navigate"
@inject NavigationManager NavigationManager

<h1>Navigate in Code Example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        NavigationManager.NavigateTo("counter");
    }
}

The following component handles a location changed event by subscribing to NavigationManager.LocationChanged. The HandleLocationChanged method is unhooked when Dispose is called by the framework. Unhooking the method permits garbage collection of the component.

@implements IDisposable
@inject NavigationManager NavigationManager

...

protected override void OnInitialized()
{
    NavigationManager.LocationChanged += HandleLocationChanged;
}

private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
{
    ...
}

public void Dispose()
{
    NavigationManager.LocationChanged -= HandleLocationChanged;
}

LocationChangedEventArgs provides the following information about the event:

For more information on component disposal, see ASP.NET Core Blazor lifecycle.

Query string and parse parameters

The query string of a request can be obtained from the NavigationManager's Uri property:

@inject NavigationManager Navigation

...

var query = new Uri(Navigation.Uri).Query;

To parse a query string's parameters:

@page "/"
@using Microsoft.AspNetCore.WebUtilities
@inject NavigationManager NavigationManager

<h1>Query string parse example</h1>

<p>Value: @queryValue</p>

@code {
    private string queryValue = "Not set";

    protected override void OnInitialized()
    {
        var query = new Uri(NavigationManager.Uri).Query;

        if (QueryHelpers.ParseQuery(query).TryGetValue("{KEY}", out var value))
        {
            queryValue = value;
        }
    }
}

The placeholder {KEY} in the preceding example is the query string parameter key. For example, the URL key-value pair ?ship=Tardis uses a key of ship.