A EditForm is a Blazor component that creates a form and distributes its editing context to descendant fields.
So far, if we wanted an input, we used the HTML <input> tag and @bind binding. It works, yes. But a real form is much more than a bunch of loose inputs.
A form needs to know:
- Are all data fields filled?
- Are the formats valid (email, phone)?
- Has the user modified any field or is the form “pristine”?
Doing these checks manually is repetitive. EditForm coordinates the model, the field state, and validation.
What EditForm Manages
EditForm is a native Blazor component that acts as a container and orchestrator. Its mission is to manage the editing state of an object.
In an interactive component, EditForm processes submission via a C# handler without reloading the document. In static SSR, the form makes a request to the server, and the component processes the received data.
The Model
In Blazor, forms are model-based. We don’t think about “that input,” we think about “that object.”
To use an EditForm, we first need a C# class representing the data we want to capture.
public class ContactModel
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public int Age { get; set; }
}Basic Syntax
To create the form, we use the <EditForm> tag and assign our object to the Model parameter.
<EditForm Model="myContact" OnValidSubmit="SaveData" FormName="contact">
<p>Form for: @myContact.Name</p>
<button type="submit">Submit</button>
</EditForm>
@code {
private ContactModel myContact = new ContactModel();
private void SaveData()
{
Console.WriteLine("Saving to database...");
}
}When assigning the Model, Blazor internally creates an object called EditContext. This is the invisible brain that tracks which fields have changed and if there are validation errors.
Handling Submission
The three submit events offered by EditForm often cause confusion. You should not use them all at once.
OnValidSubmit
This event fires only if the form is valid. If you use validation (which we will see soon) and the user enters an incorrect email, this method will NOT execute.
This is great because inside the SaveData method, you avoid checking if (isValid). Blazor has already done it for you.
<EditForm Model="@model" OnValidSubmit="HandleValidSubmit">OnInvalidSubmit
This is the opposite. It fires when the user attempts to submit, but there are validation errors. We typically use it to show a notification like “Please review the fields in red” or to scroll to the first error.
<EditForm Model="@model" OnValidSubmit="Save" OnInvalidSubmit="ShowError">OnSubmit for Manual Control
This event fires always, whether valid or not. You cannot combine OnSubmit with OnValidSubmit or OnInvalidSubmit in the same form.
Use it only if you need total control and want to validate manually in your code.
private void HandleSubmit(EditContext context)
{
if (context.Validate()) // We validate manually
{
// Save
}
else
{
// Error
}
}Use OnValidSubmit when you want to execute the operation only after validation. Reserve OnSubmit for flows that need to control the call to EditContext.Validate().
Antiforgery Protection
In a Blazor Web App, EditForm automatically adds the antiforgery component, and the form requires a valid token. This protection is especially relevant for submissions processed by the server.
The token protects against CSRF (Cross-Site Request Forgery) attacks. Also keep app.UseAntiforgery() in the pipeline generated by the template.
Where are the Inputs?
A regular <input @bind="..."> can update the model, but it does not notify the EditContext with the field information needed for validation and automatic CSS classes.
The EditForm needs to know which inputs are modifying its model in order to mark them in red if they fail. For that, Blazor provides us with a series of wrapper components:
InputTextInputNumberInputDateInputSelect- … and more.
Complete Example
Let’s look at a functional skeleton. Although we haven’t gone deep into the Input* components yet, here you can see how the pieces fit together.
<h3>New Customer</h3>
<EditForm Model="customer" OnValidSubmit="CreateCustomer" FormName="new-customer">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-3">
<label>Name:</label>
<InputText class="form-control" @bind-Value="customer.Name" />
</div>
<div class="mb-3">
<label>Email:</label>
<InputText class="form-control" @bind-Value="customer.Email" />
</div>
<button type="submit" class="btn btn-primary">Register</button>
</EditForm>
@if (submitted)
{
<div class="alert alert-success mt-3">Customer created successfully!</div>
}
@code {
public class CustomerDto
{
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}
private CustomerDto customer = new CustomerDto();
private bool submitted = false;
private async Task CreateCustomer()
{
// Simulate an async API call
await Task.Delay(1000);
submitted = true;
Console.WriteLine($"Customer {customer.Name} processed.");
}
}