Introduction

This article is part of the Blazor series. If you are interested, take a look at the previous ones.

Routing is the engine behind the mapping of the request URLs to specific endpoints within our application. In the .NET framework, the route is defined via template together with a configuration that holds the information about the URL endpoint, parameters, parameter constraints...etc.

Blazor Server is integrated into ASP.NET Core Endpoint Routing. Visit the official docs to learn more.

As mentioned on the official docs website, at the time of writing this article

Blazor Server is supported in ASP.NET Core 3.0. Blazor WebAssembly is in preview for ASP.NET Core 3.1

therefore pick your hosting model wisely.

The Router Component

After creating the Blazor app, by default, the framework provides a Router component for us. It is located within the App.razor file in the root folder and it looks like this:

<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 can see from the code that it covers two different scenarios:

  • The component with the given route is found
  • The component with the given route is not found

These scenarios are handled by the Found and NotFound template parameters correspondingly.

In the first case, when the route is found, the RouteView component does all the magic for us. It receives the route data from the Router, along with any parameters, and based on it renders the required component together with its layout. If the component doesn't have a layout, the default one will be used. We can set the default layout with the DefaultLayout property on the RouteView component.

We can define multiple layouts in our application. In the Blazor framework, to create a layout we need to create a razor component (MyLayout.razor) that extends the LayoutComponentBase and sets the @Body somewhere within the HTML. A quick example of the HTML looks like this:

@inherits LayoutComponentBase

<header>...</header>

@Body

<footer>...</footer>

@code {
    ...
}

and in the component we set the custom layout by using the @layout directive:

@layout MyLayout

<section>...</section>

@code {
    ...
}

...Back to the routing stuff...

In the second case, when the Router is not able to find a matching component a NotFound template is used. By default, it shows the p tag, located within the LayoutView component, with a simple message informing the user that the desired page was not found.

<NotFound>
  <LayoutView Layout="@typeof(MainLayout)">
    <p>Sorry, there's nothing at this address.</p>
  </LayoutView>
</NotFound>

The LayoutView ensures that the specific layout gets rendered for the not found route. We can control which layout will be used by setting the Layout property on the component. Of course, the p tag can be replaced with the custom component, for example.

<NotFound>
  <LayoutView Layout="@typeof(MainLayout)">
    <ResourceNotFound />
  </LayoutView>
</NotFound>

The Page Components

A page component is a razor component that will be rendered when a given route is matched. We define a page component just like any other component with a simple difference where we provide the @page directive at the top of the HTML. By using this directive we are declaring the route for the given component, for example, a contact page component would look like this:

@page "/contact"

<form>...</form>

@code {
    ...
}

Now, if we navigate to the route (<domain>/contact) the given route will be matched by the Router component, and the Contact.razor page component will be rendered. Think of these components as pages that wrap other components to construct a more complex view. These components have a special folder within our application folder structures and its usually the Pages folder.

You probably have noticed the AppAssembly property on the Router component. This property tells the component where to look for the page components. The assembly will be scanned at the application startup for the purpose of finding any components with the @page directive applied.

Also, there is an option to scan other assemblies. To enable this, we can set the AdditionalAssemblies property. The value for this property should be a list (IEnumerable<System.Reflection.Assembly>) of additional assemblies that we want to scan.

The Route Parameters and Constraints

Each Blazor component can have parameters. A parameter is defined with the [Parameter] attribute placed on the desired property.

To read more about parameters check out one of the previous articles in the series

But can we pass the value for them via @page directive? Of course, we can. Each parameter within the @page directive is wrapped with curly bracers {} and the name must match the parameter defined on the component. Take a look at the example below.

@page "/edit/{id}"

...

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

At the time of writing this, optional parameters were not supported.

Next, we can constraint the id parameter to accept an integer value only. It doesn't make sense that we pass in some text as a value for the id, doesn't it? Let's update our example.

@page "/edit/{id:int}"

...

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

Managing Navigation

Two main ways of navigating between the pages are by using the NavLink component or NavigationManager class.

NavLink is a built-in component that we can use in any other component template and it will generate anchor (a) tag in the HTML for us. Attributes to pay attention to are the href and Match attributes. They represent the route URL and the match criteria whether to make the link active or not on the specific page respectively. Match attribute can contain the following values:

  • NavLinkMatch.All - make the link active only if the entire URL is matched
  • NavLinkMatch.Prefix - make the link active if any prefix of the URL is matched

Let's see it in action

<NavLink class="nav-link" href="contact" Match="NavLinkMatch.All">
  Contact
</NavLink>

If we leave the href attribute empty, the route will match the home page

This is nice, but what if we want to change the page dynamically, from the code? For these cases, we can use the NavigationManager class. This class has a built-in method called NavigateTo. To navigate, we need to provide the route as the first parameter to it, and it will do the rest. Take a look at the example below.

@inject NavigationManager NavigationManager

<span @onclick="NavigateToAbout">
  About
</span>

@code {
  private void NavigateToAbout()
  {
      NavigationManager.NavigateTo("about");
  }
}

Ok, with this I will wrap up the article. I think it should get you started with Blazor routing.

Conclusion

That's all I have for this Blazor related article. If you like what you see, do share it, and consider buying me a coffee to keep me juiced up for more. Also, feel free to subscribe here, on devinduct, or follow me on twitter to keep up with the updates.

Thank you for reading and see you in the next article.