The two-way binding is the synchronization of a value between C# code and a UI control in both directions.
In the previous article we saw one-way binding, where data flowed from the code to the HTML. Now we also need to capture what the user types, checks, or selects.
If we only used what we know so far, we would have to do this to capture what the user types:
<input value="@Nombre" @onchange="@(e => Nombre = e.Value.ToString())" />Having to write a lambda event for each input would be a maintenance nightmare. Fortunately, Blazor provides us with the Two-Way Binding via the @bind directive.
The @bind directive
When we use @bind, we are telling Blazor: “Keep this C# variable and this HTML input perfectly synchronized. If one changes, update the other.”
<h3>Profile Editor</h3>
<div class="mb-3">
<label>Name:</label>
<input class="form-control" @bind="UserName" />
</div>
<div class="mb-3">
<label>Age:</label>
<input type="number" class="form-control" @bind="UserAge" />
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" @bind="IsActive" />
<label class="form-check-label">Active User</label>
</div>
<div class="mt-3">
<p>Hello, <strong>@UserName</strong>. You are @UserAge years old.</p>
<p>Status: @(IsActive ? "🟢 Active" : "🔴 Inactive")</p>
</div>
@code {
private string UserName = "Luis";
private int UserAge = 35;
private bool IsActive = true;
}Notice the elegance of the system:
- We did not have to manually convert
stringtointfor the age. Blazor does it for us. - If
UserAgechanges in the code, the input updates. - If the user changes the input,
UserAgeupdates.
What @bind generates
@bind is syntactic sugar around the control’s value and its change event.
When the Razor compiler sees this:
<input @bind="Texto" />It conceptually generates something like this (the actual code includes conversion and error handling):
<input value="@Texto" @onchange="@((ChangeEventArgs __e) => Texto = __e.Value.ToString())" />Understanding this is important when we need to customize the behavior, as we will see right now.
Controlling the update moment (@bind:event)
By default, @bind updates the variable when the input loses focus (onchange event).
Test it: type in a bound input. You will see that the text below (<p>Hello @UserName</p>) does not change letter by letter, but only when you click outside or press Tab.
What if we want real-time search? Or instant validation?
We need to change the triggering event to oninput. For that we use the @bind
<h3>Real-time Search</h3>
<input class="form-control"
@bind="SearchTerm"
@bind:event="oninput"
placeholder="Type to search..." />
<p>Searching: @SearchTerm</p>
@code {
private string SearchTerm { get; set; } = string.Empty;
}Now, the SearchTerm variable updates with each keystroke.
Watch out for performance:
Using oninput causes an update with every change. If you also trigger searches or expensive work, apply debounce or cancellation to avoid starting an operation for each keystroke.
Date formatting with @bind:format
Working with dates in inputs (<input type="date">) is often a headache because the browser expects a specific format (yyyy-MM-dd), regardless of your user’s culture.
Blazor helps us with the @bind
<label>Date of Birth:</label>
<input type="date"
@bind="BirthDate"
@bind:format="yyyy-MM-dd" />
<p>You were born on: @BirthDate.ToShortDateString()</p>
@code {
private DateTime BirthDate = DateTime.Today;
}By specifying the format, Blazor handles parsing the browser’s input and correctly converting it to your DateTime object, and vice versa.
Dropdown lists
Binding works perfectly with <select> elements, for both single and multiple selections.
<select class="form-select" @bind="SelectedCity">
<option value="">-- Select city --</option>
@foreach (var city in Cities)
{
<option value="@city.Code">@city.Name</option>
}
</select>
<p>You selected the code: @SelectedCity</p>
@code {
private string SelectedCity = string.Empty;
// Simple class City { Code, Name }
private List<City> Cities = [];
}Blazor is smart enough to select the correct <option> when loading the page based on the initial value of SelectedCity.
Binding in custom components
So far we have talked about HTML elements. But can we do Two-Way Binding between two Blazor components? Can a parent “bind” to a child’s data?
Yes, but it requires following a strict naming convention.
If the child has a Value parameter, it must have an event named ValueChanged.
Child (CustomInput.razor):
<input value="@Value" @oninput="OnInputChanged" />
@code {
[Parameter] public string Value { get; set; } = string.Empty;
// Convention: ParameterName + Changed
[Parameter] public EventCallback<string> ValueChanged { get; set; }
private async Task OnInputChanged(ChangeEventArgs e)
{
// Notify the parent of the change
await ValueChanged.InvokeAsync(e.Value.ToString());
}
}Parent:
<CustomInput @bind-Value="MyVariable" />