Application URL Routing
.NET .NET Core ASP.NET Core Blazor C# Razor Routing Visual Studio

How to Add Page Navigation and Routing in ASP.NET Core Blazor Server Applications

Welcome to today’s post.

In today’s post I will be explaining how page navigation and routing within an ASP.NET Core Blazor application works.

In a previous post, I gave an overview of the changes in the architecture of routing from ASP.NET 3.5 until ASP.NET Core. I gave an overview of routing by convention and compared that to attribute routing. I also discussed routing in ASP.NET Core web applications. 

In previous posts, I showed how to construct a data-aware ASP.NET Core Blazor Server web application. What I didn’t show was the mechanics behind the routing. In the overview that follows, I will take components of the application and show how routing and navigation connects them together.

I will give an overview comprising the following main sections:

  1. Default Routing in a Blazor Application
  2. How Blazor Routing uses Page Navigation
  3. Page Navigation with NavigationManager

Default Routing within an ASP.NET Core Blazor Web Application

With a Blazor application, much of the details on routing are quite transparent and we do not need to create exotic routing tables to determine the URLs of individual components, however a basic knowledge of how routing URIs map to destination Razor pages is required.

I will first explain how routing works within the framework of a Blazor application.

How Blazor Detects the Default Navigation Page

When the application first runs, it looks for the App.razor page, which is the root page that seeks a navigation page that is of type MainLayout. The App.razor page is auto-generated for you from the Blazor Server application project template in Visual Studio 2022.

The App.razor page source is shown below:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

The main layout page, MainLayour.Razor, which is inherited from LayoutComponentBase is shown below:

@inherits LayoutComponentBase

<PageTitle>BookLoanBlazorServerApp</PageTitle>

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

The main layout page is a container for the navigation menu side bar and the body content of the web page.

The navigation menu component, NavMenu is automatically generated and can be customized to include any additional URL route links to Razor pages within our application.

Below is the navigation menu Razor component:

<div class="top-row ps-3 navbar navbar-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="">BookLoan Library App</a>
        <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</div>

<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="createbook">
                <span class="oi oi-plus" aria-hidden="true"></span> Create a Book
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="listbooks">
                <span class="oi oi-plus" aria-hidden="true"></span> List Books
            </NavLink>
        </div>
    </nav>
</div>

@code {
    private bool collapseNavMenu = true;

    private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

In the next section I will show how Blazor Routing uses Page Navigation and how component routing is controlled within a Razor page component.

How Blazor Routing uses Page Navigation

Notice that we can see some navigational links which as you might have guessed, refer to actual Razor page components. Below is one example:

<NavLink class="nav-link" href="createbook">

The href attribute value is not an actual web page, but a reference to a Page name. I will show how page names are specified within Razor components in the next section.

Controlling Component Routing with the @page Directive

The default page for the application is specified with the forward slash ‘/’ for the request. This would be:

http://[server-name]/

To match the default page in a component, we would specify it as:

@page "/"

The index Razor component page, Index.razor is shown below:

@page "/"

<PageTitle>BookLoan Index Page</PageTitle>

<h1>Welcome!</h1>

Welcome to the BookLoan Library App!

<SurveyPrompt Title="How is the app working for you?" />

Note that if we used the same page match URI in more than one Razor page component, then we would get a routing error if we attempted to make a request through that duplicated URI. We obviously cannot browse to two pages with the same matching routing template.

When a non-blank value is specified in the href attribute in the NavLink element, the routing mechanism will attempt to match the same value in each @page directive within each Razor page in the Pages folder. 

When we click on the nav link ‘listbooks’. The route will match the ListBooks.Razor page as that contains the matching value in the @page directive:

@page "/listbooks"
@using BookLoanBlazorServerApp.Services
@using BookLoanBlazorServerApp.Models
@inject IBookService LibraryBookService

<PageTitle>List Books</PageTitle>

<h3>List Books</h3>
..

In addition to the landing page’s navigation menu, there is another place where we can navigate to different pages within the application. I will show where this is in the next section.

Controlling Component Routing with Query Strings

Within the ListBooks.razor component, we can navigate to a page that takes an additional parameter id. This is done by using the Query String syntax within the URI. So, for example, when we wish to open a book to view its details, we pass the book identifier key as shown in the HREF below:

href="viewbook/?id=@book.ID"

In a table of records this is shown below:

<h3>List Books</h3>

<h3>List Books</h3>
..
    @foreach (var book in books)
    {
        <tr>
            <td>@book.Title</td>
            <td>@book.Author</td>
            <td>@book.ISBN</td>
            <td>@book.YearPublished</td>
            <td>@book.Genre</td>
            <td>
                    <a href="editbook/?id=@book.ID">Edit</a>
             </td>
             <td>
                     <a href="viewbook/?id=@book.ID">View</a>
             </td>
        </tr>
 }
.. 

In the above we saw how to navigate to a destination Razor page component from within a Razor component. In the next section I will show how to process input parameters from the query string within the destination component.

Reading Routing Input Parameters within a Razor Page Component

To extract query string parameters from a request URL, a Razor component will need to use the [Parameter] and [SupplyParameterFromQuery] attributes before specifying a public declaration of the parameter. In the case of the ViewBook.razor component, which takes an ID integer input from the URI query string, the input parameter is declared within the @code segment as shown:

[Parameter]
[SupplyParameterFromQuery]
public int ID { get; set; }

The input parameter is then accessible to the Razor markup and to other methods within the @code segment.

@page "/viewbook"
@using Microsoft.AspNetCore.Components
@using BookLoanBlazorServerApp.Services
@using BookLoanBlazorServerApp.Models
@inject IBookService LibraryBookService
@inject ILoanService LibraryLoanService
@inject BorrowState BorrowState
@inject NavigationManager PageNavigation

&lt;PageTitle>View Book&lt;/PageTitle>

&lt;h3>View Book&lt;/h3>

@if (book == null)
{
    &lt;p>&lt;em>Loading...&lt;/em>&lt;/p>
}
else
{
 ...

@code {
    [Parameter]
    [SupplyParameterFromQuery]
    public int ID { get; set; }
…

n the next section, I will show how to navigate to Razor page components using the NavigationManager class.

Page Navigation with NavigationManager

We have seen earlier how to navigate from one page to another through the use of the href attribute within a <NavLink..> element or a hyperlink <a..> element.

There is another way in which we can navigate from one page to another, and it involves using the Blazor NavigationManager class within a Razor component. To use the NavigationManager class, we need to inject it into our component using the @inject directive. This is done as shown:

@inject NavigationManager PageNavigation

Once it is available for use, we can then use the NavigateTo() method of the PageNavigation class to redirect to another page using a URI routing template. Below is an example of how we do this using a URI route with a query string parameter:

PageNavigation.NavigateTo($"statuspage/?id={bookId}");

Below is a partial code example of how we inject the navigation manager, then use the navigation manager to redirect to a new page once a Book record has been saved successfully:

@page "/createbook"
@using Microsoft.AspNetCore.Components
@using BookLoanBlazorServerApp.Services
@using BookLoanBlazorServerApp.Models
@inject IBookService LibraryBookService
@inject NavigationManager PageNavigation

<PageTitle>Create Book</PageTitle>

<h3>Create Book</h3>
    ..
    <button class="btn btn-primary"
        @onclick="SaveBook" disabled="@isFormInvalid">
        Save Book
    </button>
</EditForm>

<br />

@code {
    ..
    // Create entered book.
    private async void SaveBook()
    {
        var bookId = await LibraryBookService.SaveBook(createbook!);
        PageNavigation.NavigateTo($"statuspage/?id={bookId}");
    }
    ..

The above example has shown how to use page navigation to perform routing within ASP.NET Core Blazor applications. In addition, I showed how to pass parameters from one page to another Razor page component.

In a future post I will show how to share and manage application state between Razor components in a Blazor application.

That is all for today’s post.

I hope you have frond this post useful and informative.

Social media & sharing icons powered by UltimatelySocial