Using the query string to pass values is a common pattern. It is useful for search parameters, form defaults, and sharing links to specific resources.
Blazor does not provide a built-in way to read query string values and bind them to component properties. However, you can retrieve the full URL using NavigationManager and the appropriate event in the Blazor component lifecycle to set those properties.
The final code looks like this:
Razor
@page "/"
@inject NavigationManager NavigationManager
<div>@Value</div>
<button type="button" @onclick="UpdateValue">Update value</button>
@code
{
// 👇 The value is set from the query string by the SetParametersAsync method
[QueryStringParameter]
public int Value { get; set; }
public override Task SetParametersAsync(ParameterView parameters)
{
// 👇 Read the value of each property decorated by [QueryStringParameter] from the query string
this.SetParametersFromQueryString(NavigationManager);
return base.SetParametersAsync(parameters);
}
private void UpdateValue()
{
Value = new Random().Next();
// 👇 Update the URL to set the new value in the query string
this.UpdateQueryString(NavigationManager);
}
}
First, create the QueryStringParameterAttribute to decorate the properties you want to bind from the query string:
C#
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class QueryStringParameterAttribute : Attribute
{
public QueryStringParameterAttribute()
{
}
public QueryStringParameterAttribute(string name)
{
Name = name;
}
/// <summary>Name of the query string parameter. It uses the property name by default.</summary>
public string Name { get; }
}
Next, write the extension methods to parse the query string and assign each parameter to the corresponding property. Parsing and editing the query string is straightforward thanks to QueryHelpers from the Microsoft.AspNetCore.WebUtilities NuGet package.
C#
// Requires Microsoft.AspNetCore.WebUtilities to edit the query string
// <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
public static class QueryStringParameterExtensions
{
// Apply the values from the query string to the current component
public static void SetParametersFromQueryString<T>(this T component, NavigationManager navigationManager)
where T : ComponentBase
{
if (!Uri.TryCreate(navigationManager.Uri, UriKind.RelativeOrAbsolute, out var uri))
throw new InvalidOperationException("The current url is not a valid URI. Url: " + navigationManager.Uri);
// Parse the query string
Dictionary<string, StringValues> queryString = QueryHelpers.ParseQuery(uri.Query);
// Enumerate all properties of the component
foreach (var property in GetProperties<T>())
{
// Get the name of the parameter to read from the query string
var parameterName = GetQueryStringParameterName(property);
if (parameterName == null)
continue; // The property is not decorated by [QueryStringParameterAttribute]
if (queryString.TryGetValue(parameterName, out var value))
{
// Convert the value from string to the actual property type
var convertedValue = ConvertValue(value, property.PropertyType);
property.SetValue(component, convertedValue);
}
}
}
// Apply the values from the component to the query string
public static void UpdateQueryString<T>(this T component, NavigationManager navigationManager)
where T : ComponentBase
{
if (!Uri.TryCreate(navigationManager.Uri, UriKind.RelativeOrAbsolute, out var uri))
throw new InvalidOperationException("The current url is not a valid URI. Url: " + navigationManager.Uri);
// Fill the dictionary with the parameters of the component
Dictionary<string, StringValues> parameters = QueryHelpers.ParseQuery(uri.Query);
foreach (var property in GetProperties<T>())
{
var parameterName = GetQueryStringParameterName(property);
if (parameterName == null)
continue;
var value = property.GetValue(component);
if (value is null)
{
parameters.Remove(parameterName);
}
else
{
var convertedValue = ConvertToString(value);
parameters[parameterName] = convertedValue;
}
}
// Compute the new URL
var newUri = uri.GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.UriEscaped);
foreach (var parameter in parameters)
{
foreach (var value in parameter.Value)
{
newUri = QueryHelpers.AddQueryString(newUri, parameter.Key, value);
}
}
navigationManager.NavigateTo(newUri);
}
private static object ConvertValue(StringValues value, Type type)
{
return Convert.ChangeType(value[0], type, CultureInfo.InvariantCulture);
}
private static string ConvertToString(object value)
{
return Convert.ToString(value, CultureInfo.InvariantCulture);
}
private static PropertyInfo[] GetProperties<T>()
{
return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
private static string GetQueryStringParameterName(PropertyInfo property)
{
var attribute = property.GetCustomAttribute<QueryStringParameterAttribute>();
if (attribute == null)
return null;
return attribute.Name ?? property.Name;
}
}

When clicking on the update button, you can see that the URL is updated:
Note that NavigationManager.NavigateTo triggers Blazor's navigation handler, which rebinds parameters and re-renders the component. If you only want to update the URL without triggering Blazor navigation, you can use the history API in JavaScript:
C#
// @inject IJSRuntime jsRuntime
await jsRuntime.InvokeVoidAsync("window.history.replaceState", null, "", newUri);
Do you have a question or a suggestion about this post? Contact me!