Learn how to implement child routes and use optional route parameters in Blazor for more advanced routing needs.
In this article, we explore child routes and optional route parameters in Blazor.
In a previous article of this Blazor Basics series, we learned the Blazor Routing and Navigation Fundamentals. That post would be a good place to start if you haven’t read it yet before we get into the more advanced routing options in this article.
Routing and navigation are fundamental problems solved by modern single-page web application frameworks.
Static routes and simple parameter passing are often trivial and can be expressed with a few lines of code. However, it becomes interesting, more complex and often more code-intensive to solve advanced concepts, such as defining and using child routes and optional route parameters.
In this article, you will learn how we can implement advanced routing concepts using Blazor.
A child route is a route within another route. Sometimes, it’s also referred to as a nested route.
Blazor doesn’t have child routes, as you might know them from React or Angular, where router implementations offer a tree-like structure. However, we do not need real child routes in most use cases. Instead, we can use the regular Blazor way of defining routes using the @page
directive.
The following code in an Order
component shows a child route definition:
@page "/orders/{OrderId:int}/product/{ProductId:int}"
We have a parent route orders that accepts an int
parameter OrderId
. The child route for the product has a ProductId
of type int
as its parameter.
Instead of nesting the route definitions, we need to extend the route definition for every level we want to nest deeper.
Now take a look at the following Order
component of a real-world scenario representing a single order in an ecommerce system:
@page "/orders/{OrderId:int}"
<h3>Order</h3>
<p>OrderId: @OrderId</p>
<h4>Products</h4>
<ul>
<li><a href="/orders/@OrderId/product/42">Product 42</a></li>
</ul>
@code {
[Parameter]
public int OrderId { get; set; }
}
We provide the OrderId
as the route parameter. We imagine there is an Orders
page, which contains all orders, and this Order
component displays a single order.
We also see a list of products that are listed in the order. We build a link to another page using a child route by extending the route defined at the top of this Order
page component.
Now let’s see how the ProductOrder
component is implemented:
@page "/orders/{OrderId:int}/product/{ProductId:int}"
<h3>Product Details</h3>
<p>OrderId: @OrderId</p>
<p>ProductId: @ProductId</p>
<p>Go to <a href="/orders/@OrderId">Order @OrderId</a></p>
@code {
[Parameter]
public int OrderId { get; set; }
[Parameter]
public int ProductId { get; set; }
}
We have the route definition, which has the ProductId
at the end, matching the URL we constructed on the previous page to navigate to this page.
In the code section, we need to implement two properties. Each property holds the information of one of the route parameters.
At first, this might look like reinventing the wheel and typing the same code over and over again for each child’s route. While that is true, it is still an elegant and simple solution to express what we need in most situations.
It could be that we get real child routes in Blazor in the future. However, there has been a discussion going on for years, and the design and implementation of that feature have been postponed multiple times.
Suppose you are a React or Angular developer transitioning into Blazor and want to use true nested routes as they are available in those frameworks. In that case, there is an open-source third-party Blazor Router implementation by a community member.
The main reason for the delay is that the current router is simple and flexible, and it solves 99% of developers’ needs.
The following type constraints are available in Blazor for route definitions:
I highly recommend using type constraints for all route definitions. If we do not specify a type constraint, Blazor treats the parameter as a string.
Now that we understand Routing in Blazor and have learned how to implement child routes, we are ready to explore optional route parameters.
Similar to child routes, Blazor doesn’t explicitly offer a feature to implement optional route parameters. However, once again, we can reuse an existing mechanism to do so.
Consider the following OptionalParameters
component.
@page "/optional"
@page "/optional/{id:int}"
@page "/optional/{title}"
We define three different routes using the @page
directive. Yes, we can use the @page
directive multiple times on the same page. We register the page for different routes.
We access the values from the parameters as we access them when defining any parameterized route using properties in the code section.
@code {
[Parameter]
public int Id {get;set;}
[Parameter]
public string Title {get;set;}
}
The template code looks like this:
class="prism language-html"><p>Id: @Id<br />
Title: "@Title"</p>
<ul>
<li><a href="/optional">Optional</a></li>
<li><a href="/optional/11">Optional/11</a></li>
<li><a href="/optional/my-title">Optional/my-title</a></li>
</ul>
For demonstration purposes, we implement the page with an Id
and a Title
property, both of which are rendered on the screen.
We use three links to reach the different routes.
Optional route parameters allow you to implement dynamic scenarios where you want to be able to navigate to a page from different angles with different parameters.
Besides having one or multiple optional route parameters, we can also define the data type for each parameter using type constraints.
When working with optional route parameters, the property contains the default C# value of the type. In the case of a string
, the property will contain an empty string if no value is provided. In the case of an int
, the value will be 0
.
However, we can specify the default value of optional route parameters in the component’s code.
First of all, we need to define the property as a nullable type. In this example, we change the definition of the Id
property to a nullable int
.
[Parameter]
public int? Id {get;set;}
Next, we override the OnInitialized
lifecycle method and set the default value if the variable is null
.
protected override void OnInitialized()
{
Id = Id ?? 16;
}
This implementation ensures that the Id
property contains either a value provided by the route parameter or the default value set in the statement within the OnInitialized
method.
Blazor doesn’t provide a specific syntax to define child routes. However, we can reuse the @page
directive to define a nested route.
Optional route parameters allow us to register a page with optional arguments and for multiple different parameter types.
Blazor supports nine different type constraints for route parameters. Blazor will treat the parameter as a string
if we don’t specify the type.
We can specify default values when using optional route parameters by overriding the OnInitialized
lifecycle method. In this case, we need to make sure that we use nullable properties.
You can access the code used in this example on GitHub.
If you want to learn more about Blazor development, you can watch my free Blazor Crash Course on YouTube. And stay tuned to the Telerik blog for more Blazor Basics.
Claudio Bernasconi is a passionate software engineer and content creator writing articles and running a .NET developer YouTube channel. He has more than 10 years of experience as a .NET developer and loves sharing his knowledge about Blazor and other .NET topics with the community.