Routing In Blazor

Introduction

Have you ever wondered how to navigate web pages? The answer is URL which refers to "Uniform Resource Locator", URL is a unique address associated with each website.

This is where routing comes in. Every page on the website, or more accurately every component in Blazor has a unique locator, with the locator you can achieve routing in Blazor. Well routing in general is a huge topic but I'll do my best to explain to you fundamental concepts of routing in Blazor.

This is chapter 12 in our journey to learn Blazor, Here is my publicly available repository with all source code available. I like to call it BlazingBlazor.

Following are the chapters I've covered so far. Have a peek.

Okay, that said, let's get straight to the action.

Let's create a Blazor webassembly project that comes out of the box. Once you've done that hit the run button, once the project is running the following page will appear. What is this page? 

Let me explain you anatomy of URL first,

  • localhost is my local machine, this is usually where the name or IP address of your application server comes.
  • Followed by the port number on which this app is running.

As you can see, this home page runs by default, this is called the Index-page.

Routing 101 in Blazor
Figure 1: The Index page

The Index page

Figure 2 shows the default location of Index.razor in the solution.

Routing 101 in Blazor
Figure 2: Index.razor's location

The following listing 1, shows what code is already written on index page, forget about the content of the page, what we are interested in is line number 1. which says @page.

You may ask what is this @ Page?

Well, folks that's how you achieve routing in Blazor by using the @page directory. So you use @page followed by the page name written inside quotes ("PageName").

Now you may have the most obvious question, "Rikam, you just said that name of the page goes inside ("quotes") but here in the following snippet 1 all I see is forward slash ("/") so how does it work as an index page of the entire application? and doesn't it needs a page name such as index or something along those lines?

Well, the forward slash ("/") is placed at the end of a URL such as domain.com/ or domain.com/page/. Here the reference to index page is always made with just forward slash. This indicates to an application that this is the home page and that's why you are seeing Index.razor as start of the application

Tip no one asked for:  A Component in Blazor could have a route or could not have a route, if a component has a route it's called a routing component.

@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />

Listing 1: Index.razor

You cannot have 2 Index pages in one application!

Let me correct that, you cannot have 2 pages sharing same routes!!

Of course, you can't load multiple pages at once and call it start of the application. Why, because Blazor is SPA - Single-Page Application, Learn more here.

But let me prove it to you, say I go to Counter.razor page and change its page directory to @page "/", now I've following 2 pages acting as index page.

  1. Index.razor
  2. Counter.razor
@page "/"
<PageTitle>Index</PageTitle>

Listing 2: Index.razor 

@page "/"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>

Listing 3: Counter.razor

In this scenario, Blazor will elegantly throw me following error, saying that routes are ambiguous. So now you know one application must always have only one home page.

Routing 101 in Blazor
Figure 3: Two components with same page route

One page can have multiple routes

There is a special case where we need this, but first let me demonstrate how it works.

Let me add another route to Counter.razor, which would look something like listing 4.

@page "/counter"
@page "/counterAttack"

<PageTitle>Counter</PageTitle>
<h1>Counter</h1>

Listing 4: Counter.razor

And see how totally acceptable it is. See the following gif for validation. Both "/counter" and "/counterAttack" lands to the same page.

Routing 101 in Blazor
Gif 1: Multiple routes for same page

Also notice at the end, I changed the case of counter to Counter then again to CounTER, and it still worked. because Routing is case-insensitive

Sending routing parameters 

There might be situations where you need to send parameters from the URL to your component. You can easily do that. Let's modify the counter component to understand this.

Add another new routing parameter called "NameOfProject", before I show this to you let me get you familiar with 2 rules that has to be followed.

  • 1. The property with the same name as parameter name must be available in the "@Code" part. Here in Listing 6, at line number 13, I have a C# property which holds the value that we are going to pass through URL.
    • If you don't respect rule number 1, you will get a property that doesn't match the exception.

Routing 101 in Blazor
Figure 4: Counter does not have property matching

  • 2. You need to decorate this property with [Parameter] or [CascadingParameter]
    • If you don't respect rule number 2, you'll get required attribute exception.

Routing 101 in Blazor
Figure 5: Counter has property but does not have attribute

Here is the syntax to pass the parameters.

@page "/counter/{NameOfProject}"

Listing 5: Counter.razor

Also, I am printing the same value of NameOfProject in the <h1> tag at line number 5.

@page "/counter"
@page "/counter/{NameOfProject}"

<PageTitle>Counter</PageTitle>
<h1>@NameOfProject</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
    [Parameter]
    public string NameOfProject {
        get;
        set;
    }
    public int currentCount {
        get;
        set;
    }
    private void IncrementCount() {
        currentCount++;
    }
}

Listing 6: Counter.razor

When you run the application, you'd be able to pass string parameters through URL and observe how I can add space in parameter ("Blazing Blazor") and URL takes care of it by adding "%20" in between. Very Smart!! 

Routing 101 in Blazor
Gif 2: Passing parameters through URL

Passing type-specific parameter

In the Counter component, we have the "CurrentCount-property" which is of type integer. All we have to do is to explicitly specify the type of the parameter in the page definition.

@page "/counter/{CurrentCount:int}"

Listing 7: Counter.razor

Now let's obey the same two rules that we mentioned above, add [Parameter] tag and use the same property name. After making these changes your final Counter component would look like listing 8.

@page "/counter"
@page "/counterAttack"
@page "/counter/{NameOfProject}"
@page "/counter/{CurrentCount:int}"
<PageTitle>Counter</PageTitle>
<h1>@NameOfProject</h1>

<p role="status">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
    [Parameter]
    public string NameOfProject {
        get;
        set;
    }
    [Parameter]
    public int CurrentCount {
        get;
        set;
    }
    private void IncrementCount() {
        CurrentCount++;
    }
}

Listing 8: Counter.razor

In the output, you can see how Value of the count is changing as we pass it from the URL, then the component takes the new value of the counter and increases it as I keep clicking the button.

Routing 101 in Blazor
Gif 3: Passing int or string parameters through URL 

Also notice in my page I've both parameters "NameOfProject" and "CurrentCount" and as you can see in above gif, at once I can only pass either of these parameters.

What if I want to send both of these parameters at the same time? Alright we got a solution for that as well.

Sending multiple parameters at the same time

Now in order to send multiple parameters, you need to append one more parameter to route, as follows.

First parameter is string and second parameter is of int type.

@page "/counter/{NameOfProject}/{CurrentCount:int}"

Listing 9: Counter.razor

 Here is the full picture of counter component.

@page "/counter"
@page "/counterAttack"
@page "/counter/{NameOfProject}"
@page "/counter/{CurrentCount:int}"
@page "/counter/{NameOfProject}/{CurrentCount:int}"
<PageTitle>Counter</PageTitle>
<h1>@NameOfProject</h1>

<p role="status">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
    [Parameter]
    public string NameOfProject {
        get;
        set;
    }
    [Parameter]
    public int CurrentCount {
        get;
        set;
    }
    private void IncrementCount() {
        CurrentCount++;
    }
}

Listing 10: Counter.razor

See how it is working smoothly for multiple parameters.

Routing 101 in Blazor
Gif 4: Passing multiple parameters at once through URL 

Query Parameters

Query parameters are appended at the end of a URL. You can pass the data through a query and use that data in the component.

First comes your server name followed by name of the page followed by question mark ("?") followed by parameters.

Here is an example

https://localhost:7276/counter?param1=BlazingBlazor&param2=999

Listing 11: Counter.razor

Here, I am passing 2 parameters. The first one is string and next one is int type. 

The beauty of this is that you don't have to expose parameters to the outside world like we did above or use [Parameter] attribute.

But we need to add few things in order to access query params,

You need to add System.web namespace and inject NavigationManager. Will learn more about navigation manager in upcoming chapter.

@using System.Web
@inject NavigationManager navigation;

Listing 12: Counter.razor

Now you need to parse the query and extract parameters. Here if you look closely at line number 3 & 4 I am using "param1" & "param2" as keys to access query-parameters values because those are the names I passed in above query specified in listing 11.

Uri currentUri = new Uri(navigation.Uri);
var parameters = HttpUtility.ParseQueryString(currentUri.Query);
NameOfProject = parameters["param1"];
CurrentCount = Convert.ToInt32(parameters["param2"]);

Listing 12: Counter.razor

Let me give you full picture of Counter.razor

@page "/counter"
@using System.Web

@inject NavigationManager navigation;

<PageTitle>Counter</PageTitle>
<h1>@NameOfProject</h1>
<p role="status">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
    private string NameOfProject {
        get;
        set;
    }
    private int CurrentCount {
        get;
        set;
    }
    private void IncrementCount() {
        CurrentCount++;
    }
    protected override void OnInitialized() {
        Uri currentUri = new Uri(navigation.Uri);
        var parameters = HttpUtility.ParseQueryString(currentUri.Query);
        NameOfProject = parameters["param1"];
        CurrentCount = Convert.ToInt32(parameters["param2"]);
    }
}

Let's run this app with respective changes as above listing and see it living its life.

Routing 101 in Blazor
Gif 5: Passing query parameters

Conclusion

We started this article by understanding routing in general, we learned how to implement routing in Blazor. How to use multiple routes, how to pass data-type specific parameters through routing, what are query parameters and how to use them in Blazor then especially we learned, what not to do while implementing routing.

That's a lot to digest, but with the practical implementation you'll get the hang of it.

You can find source code of all chapters: In this repository

Thank you all, Code for better future!!