Blazor

Blazor Bites - Routing

Blazor Bites Series


Post valid for: Blazor 0.6.0
Please remember that Blazor is an experimental project and is going to be changing regularly. IT IS NOT PRODUCTION READY.


Routing

Blazor comes with a router out of the box, similar to other SPA applications such as Angular. While the Blazor router is much simpler than those found in other frameworks, you can already build useful multi-page apps.

How does the router in Blazor actually work?

Blazors router is just another component and implements IComponent the same as any other. Currently it takes an AppAssembly parameter which is the assembly for the Blazor application. However, this is temporary and in the future this will be specified in the Program.cs.

The router uses the provided AppAssembly to find the component which matches the URL of the current request, then it loads that component.

Route Templates

In Blazor, you define routes using route templates. You can define a route template by adding the @page directive to the top of a component.

@page "/home"

<h1>Hello World</h1>

The above component would be loaded when the user navigated to www.mydomaim.com/home.

If you are defining your component as a pure C# class then you can specify its route template by decorating it with the route attribute, [RouteAttribute("/home")]. Which is ultimately what the @page directive gets compiled to.

It is also valid to specify multiple route templates for a component. You can achieve this by defining multiple @page directives or [RouteAttributes].

@page "/"
@page "/home"

<h1>Hello World</h1>

Route Parameters

The example above is fine for simple routes but what if you need to pass some data via the URI? For example, you have a product component that needs a product id to load that products information. That is where route parameters come in.

When defining a route template you can use curly brackets to include a parameter, @page "/products/{ProductId}". This parameter is then assigned to a property of the same name on the component.

@page "/products/{ProductId}"
@inject IProductService ProductService

...

@functions {

    [Parameter]
    private int ProductId { get; set; }
    
    private Product product;
    
    protected override async Task OnInitAsync()
    {
        product = await ProductService.GetProduct(ProductId);
    }

}

Similar to routes in ASP.NET Core, you can also define route constraints on route parameters. In the example above you may wish to enforce that the ProductId was an int. In order to achieve this you could change the route template as follows, @page "/products/{ProductId:int}". If someone then tried to navigate to this component with a URI like /products/foo then the router would not match the route with the above component. The currently supported types for enforcement are:

  • bool
  • datetime
  • decimal
  • double
  • float
  • guid
  • int
  • long

Linking Pages

There are three ways to link pages, one is to use regular a tags,  another is to use the NavLink component, the last is programmatically.

To use traditional a tags you just have to specify the relative URI to the page you wish to link to. You will need to have a base tag defined in your index.html, but if you are using one of the Blazor templates this is already done for you. Then Blazors router will automatically handle navigation for you without causing any postbacks.

The next option is to use the NavLink component provided by Blazor. It takes a href as a parameter which it then uses to render a standard a tag. But whats really useful is that when the current URI matches the href it will add an active class to the link.

<!-- Defining link -->
<NavLink href="/home">
    Home
</NavLink>

<!-- Rendered link not matching current URI -->
<a class="null" href="/home">Home</a>

<!-- Rendered link matching current URI -->
<a class="active" href="/home">Home</a>

You can also define a Match parameter on a NavLink which tells the component how to decide if the current URI matches the href. There are currently two options available. All and Prefix. All is the default and tells the NavLink component to apply the active class only when the whole URI matches. The second option is Prefix and this tells the NavLink component to apply the active class when the prefix of the current URI matches. This is useful when you have a menu with sub sections where you may wish to apply styling to the section link and the currently active sub-section link.

<NavLink href="/expenses" Match=NavLinkMatch.Prefix>Expenses</NavLink>
<NavLink href="/expenses/shopping" Match=NavLinkMatch.All>Shopping</NavLink>
<NavLink href="/expenses/bills" Match=NavLinkMatch.All>Bills</NavLink>
<NavLink href="/expenses/groceries" Match=NavLinkMatch.All>Groceries</NavLink>

In this example, when the /expenses/bills URI was requested both the Expenses and Bills links would have the active  class applied.

The final way to is to navigate programmatically. In order to do this you will need to use IUriHelper. This helper contains a few handy methods but the one we're interested in is NavigateTo. This method takes a string, which is the URI to navigate to, then performs the navigation.

UriHelper.NavigateTo("/home");

In order to use it you will need to inject it into your component or service. To inject into a component you can either use the @inject directive, or the [Inject] attribute.

<!-- cshtml component -->
@inject IUriHelper UriHelper
// C# only component
public class MyComponent : BlazorComponent
{
    [Inject]
    public IUriHelper UriHelper { get; set; };
    
    ...
}

If you need to use it from somewhere other than a component such as a service then you must use constructor injection.

public class MyService 
{
    private IUriHelper _uriHelper;
    
    public MyService(IUriHelper uriHelper)
    {
        _uriHelper = uriHelper;
    }
    
    ...
}

Wrapping up

I hope you've enjoyed this post on Blazors Routing. If you have any questions please ask them in the comments below and I'll do my best to answer them. Or if there is any particular Blazor topic you want me to cover then again, let me know in the comments.