In this article, we are going to learn about the Blazor Routing and how to enable navigation between different sections in our application. We are going to explain how to use the @page directive to support routing and how to create dynamic navigation from the code. Additionally, we are going to learn about parameters in routes and how/when to use the NavLinks component.

To download the source code for this article, you can visit the Blazor Routing repository.

For the complete navigation for this series, you can visit the Blazor Series page.

We are going to divide this article into the following topics:

About @page Directive in Blazor Routing

To be able to create the routes in our application, we have to use the routing rules. These rules help us define which component should appear for the required URL. In the Blazor application, we can set up the Blazor routing rules using the @page directive. With it, we define the routes for our components.

So, let’s inspect this directive by opening the Index.razor file:

Blazor Routing @page directive

As you can see, if we want to set up the route in the component, we have to use the @page directive followed by the route itself. For the Index page, the route is /, which means it is the starting point for our application (the root). From the previous articles, we could’ve seen that as soon as we start our application, it navigates to https://localhost:5001 and shows the Index page.

The component is not restricted to only one routing rule, we can add multiple rules as well. Let’s try that by adding a new route in the Index component:

@page "/"
@page "/index"
@using BlazorProducts.Client.Components

We can start the application and it will navigate to the localhost:5001 route. And if we add /index to it, the route will work as well:

Index route for Blazor Routing

All routes must start with the / sign. Without it, the application will report an error:

rote without slash sign

From this message, it is pretty obvious what the compiler is complaining about.

App.razor File and the NotFound Component

Let’s start our application one more time and as soon as it starts, let’s try to navigate to localhost:5001/something:

Blazor generic not found message

We can see there is no component related to our route but, the application still shows a component with the generic message.

Where does this message come from?

If we take a look at the Solution Explorer window, we are going to find the App.razor file. Let’s open it:

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

We see the Router component that handles all the routing logic in our application. Inside it, there are two components: Found and NotFound. The Found component handles all the existing components with the valid routes, and the NotFound component will trigger if the component doesn’t exist. Additionally, we can see both components using the same Layout file – MainLayout.

Now, let’s create a new CustomNotFound component in the Pages folder:

@page "/404"

<div class="card customNotFound">
    <div class="card-body">
        <div class="row">
            <div class="col">
                We are sorry, but we couldn't find the page you are looking for!!!
            </div>
        </div>
    </div>
</div>

@code {

}

We use the bootstrap classes in this file, except for the customNotFound class, which is a custom class. To support this, let’s create a new product.client.css file in the wwwroot/css folder and modify it:

.customNotFound {
    text-align: center;
    color: red;
    font-size: 35px;
    box-shadow: 1px 1px 1px grey;
}

Then, we have to modify the index.html file, to include this custom file in our application:

<link href="css/products.client.css" rel="stylesheet" />

Finally, let’s modify the App.razor file:

@using BlazorProducts.Client.Pages;

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <CustomNotFound />
        </LayoutView>
    </NotFound>
</Router>

Now, we can start our application and try to navigate to /404 address:

NotFound component in Blazor Routing

Additionally, if we try to navigate to the /something route, we are going to get the same result.

Update – CSS Isolation

From the .NET Preview 8, Blazor WebAssembly has support for the CSS Isolation. So, if we have a .css file specific just for the NotFound component, we can create the NotFound.razor.css file in the Pages folder, and place our CSS code inside it. That way, our files will be better organized and the scope of the CSS file would be aimed just to the specific component.

Using NavigationManager

It is clear how we can use links to navigate the user from one page to another. But sometimes, we want to use the code to navigate the user to other pages in the application. We can do that with the help of the NavigationManager service.

Let’s modify the CustomNotFound page by adding a new button below the current content:

@page "/404"

<div class="card customNotFound">
    <div class="card-body">
        <div class="row">
            <div class="col">
                We are sorry, but we couldn't find the page you are looking for!!!
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col text-md-center m-5 text-primary">
        <button type="button" class="btn btn-primary" @onclick="NavigateToHome">Navigate to the Home Page</button>
    </div>
</div>

And, let’s create a new partial class for this component to create the required method in:

public partial class CustomNotFound
{
    [Inject]
    public NavigationManager NavigationManager { get; set; }

    public void NavigateToHome()
    {
        NavigationManager.NavigateTo("/");
    }
}

When we look at this class, we see one big difference between the Blazor and regular ASP.NET Core applications. We have to use the [Inject] attribute to inject our services in the class and not the constructor. Once we inject the service, we use the NavigateTo method to navigate to the provided URI.

Now, if we start our application, navigate to the non-existing page and then click the button (once the not found page appears), the application is going to direct us to the Home page. Moreover, if we want to preserve the URI of the page we are currently on, before the redirection action, we can do that by using the Uri property:

NavigationManager.Uri

Excellent.

Let’s continue by showing how to pass parameters while navigating to a different page.

Route Parameters in Blazor Routing

To show that, we are going to create new files for the ReportError page in the Pages folder:

Report error files

Now, we can modify the razor file first:

@page "/reportError/{errorCode:int}/{errorDescription}"

<h3>
    The error with the @ErrorCode code and @ErrorDescription description has been reported.  
</h3>
<p>
    Click <a href="/">here</a> to navigate to the Home page.
</p>

So, we create our route for this component with two parameters. The first parameter is of type int and the second one of type string. We can see that for the string parameter we don’t have to assign the type. Furthermore, in the h3 tag, we use two properties that will accept the values from our route parameters.

That said, let’s create them in the .cs file:

public partial class ReportError
{
    [Parameter]
    public int ErrorCode { get; set; }

    [Parameter]
    public string ErrorDescription { get; set; }
}

Finally, we have to modify the CustomNotFound component:

@page "/404"

<div class="card customNotFound">
    <div class="card-body">
        <div class="row">
            <div class="col">
                We are sorry, but we couldn't find the page you are looking for!!!
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col text-md-center m-5 text-primary">
        <button type="button" class="btn btn-primary" @onclick="NavigateToHome">Navigate to the Home Page</button>
    </div>
</div>
<div class="row">
    <div class="col">
        If you want, you can report this error by clicking the following link: <a href="/reportError/404/The page is missing">Report an error</a>
    </div>
</div>

After all of the changes, we can test our application.

Let’s start it, navigate to the non-existing page, and click the “Report an error” link:

Report error page working

And there we go. We can see the URI with two parameters and also both parameter values assigned to the respective properties.

If we click the here link, we are going to be directed to the Home page.

Let’s talk a bit about the NavLink component.

As soon as we navigate to any of our menu items, that item is going to be highlighted. So, let’s inspect the current link, to see what is going on there:

Home highlighted

We can see the Home component is the active one and in the Dev tools window, we can see that the link has the nav-link and active classes assigned. The NavLink component toggles that active class to our menu item links. If the link is active the class will be present. It is important to mention that we should use the NavLink component only for the navigation menu and not for the links inside the content of the page.

Moreover, let’s open the NavMenu file and inspect the code:

NavMenu component

We can see the NavLink component in each of our links and the nav-link class but without the active class. The NavLink component adds this class dynamically to our active link. Furthermore, we can see the Match attribute. NavLink uses this attribute to configure the match options. There are two options for that:

  • NavLinkMatch.All,
  • And NavLinkMatch.Prefix

With the first option, the link will be active only if it matches the entire URL. With the second option, which is the default one, the link will be active if it matches any prefix of the current URL. So, if we inspect the counter link, we can see that it doesn’t have the Match attribute. This means the link is going to be active whether the route to this page is https://localhost:5001/counter or https://localhost:5001/counter/something.

Conclusion

Excellent.

We have learned:

  • How to create routes in our application
  • The way to use the NavigationManager to navigate dynamically from the code
  • How to pass parameters through the routes
  • And more about the NavLink component in Blazor WebAssembly

In the next article, we are going to learn about HTTP calls and how to fetch data with Blazor WebAssembly from the WebAPI server.