NavigationManager is the Blazor service that represents the current URI and allows initiating or monitoring navigation.
In the previous article, we defined routes with @page. Now we need to navigate from C# in cases like these:
- The user fills out a form and, upon saving, we want to redirect them to the list.
- The user logs in and we want to send them to the Dashboard.
- An error occurs and we want to force a page reload.
For all this, Blazor provides a fundamental injectable service: the NavigationManager.
Injecting the Service
As it is a core service of ASP.NET Core, it is already registered in the dependency injection container. We just need to request it.
In a .razor file:
@inject NavigationManager NavManagerIn a .razor.cs class (Code-Behind):
[Inject]
public NavigationManager NavManager { get; set; } = default!;Basic Navigation with NavigateTo
The main method is NavigateTo. It receives a relative or absolute URI and, when the internal destination supports enhanced or interactive navigation, avoids a full reload.
private void GoToHome()
{
// Relative navigation (recommended)
NavManager.NavigateTo("/");
// Navigation to another internal page
NavManager.NavigateTo("/products/details/5");
}Advanced Navigation Options
NavigateTo also accepts a NavigationOptions object to control the transition.
ForceLoad: Forcing a Reload
Sometimes we need to “restart” the application, for example, if the user has logged out and we want to completely clear the browser’s memory, or if we are navigating to an external page outside our SPA.
// This triggers an F5 (full refresh) in the browser
NavManager.NavigateTo("/login", forceLoad: true);ReplaceHistoryEntry: Replacing the History
Imagine a Login flow.
- User enters
/login. - They log in successfully.
- We redirect them to
/dashboard.
If the user presses “Back”, they will return to the login. To avoid this, ReplaceHistoryEntry replaces the current history entry instead of adding a new one.
NavManager.NavigateTo("/dashboard", new NavigationOptions
{
ReplaceHistoryEntry = true
});Building Query Strings
In the previous article, we saw how to read URL parameters ([SupplyParameterFromQuery]). Now we’ll see how to write them.
We could manually concatenate strings:
NavManager.NavigateTo($"/search?q={text}&page={page}");
This concatenation is dangerous (what happens if text contains spaces or special characters?). In modern Blazor, we can use the GetUriWithQueryParameters method.
private void FilterProducts()
{
// 1. Prepare the parameters in a dictionary
var parameters = new Dictionary<string, object?>
{
{ "category", "electronics" },
{ "inStock", true },
{ "sort", "price_desc" },
{ "page", 1 }
};
// 2. Generate the safe URL
// Result: /products?category=electronics&inStock=true&sort=price_desc&page=1
var url = NavManager.GetUriWithQueryParameters("/products", parameters);
// 3. Navigate
NavManager.NavigateTo(url);
}This method automatically handles character encoding (URL Encoding) and manages null values.
Reading the Current URL
Sometimes we need to know “where we are” to make logical decisions (for example, showing a button only if we are on the Home page).
NavManager.Uri: Returns the complete absolute URL (e.g.,https://localhost:5001/products?id=5).NavManager.BaseUri: Returns the site root (e.g.,https://localhost:5001/).
To get the relative path (what we usually need), we can use ToBaseRelativePath:
var absolute = NavManager.Uri; // https://mysite.com/admin/users
var relative = NavManager.ToBaseRelativePath(absolute); // admin/usersListening to Navigation Events
The NavigationManager is not only for acting, but also for reacting. We can subscribe to the LocationChanged event to know when the user has changed pages.
This is very useful for analytics systems, logging, or for closing dropdown menus when the route changes.
@implements IDisposable
@inject NavigationManager NavManager
<p>You are at: @currentUrl</p>
@code {
private string currentUrl = string.Empty;
protected override void OnInitialized()
{
currentUrl = NavManager.Uri;
// Subscribe to the event
NavManager.LocationChanged += OnLocationChanged;
}
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
currentUrl = e.Location;
Console.WriteLine($"The user has navigated to: {e.Location}");
// Important: StateHasChanged is needed here because the event
// can fire outside the normal render lifecycle of this component.
_ = InvokeAsync(StateHasChanged);
}
public void Dispose()
{
// Unsubscribe so the service doesn't hold onto the component
NavManager.LocationChanged -= OnLocationChanged;
}
}If you subscribe to LocationChanged and do not unsubscribe in Dispose, your component will remain “alive” in memory even if the user changes pages, consuming resources and potentially causing errors.