Are you sure your Blazor forms are validating the right way? Many developers rely solely on built-in validation messages and hope for the best. But here’s the thing — without a deeper understanding of how EditForm
validation truly works in Blazor, you might be missing critical behavior, especially in real-world apps.
Let’s demystify the whole validation flow of EditForm
in Blazor, clarify the rules behind DataAnnotationsValidator
, ValidationSummary
, ValidationMessage
, and most importantly — show how you can take control of the process and inject your own custom validation logic.
What is EditForm
in Blazor?
EditForm
is a core component in Blazor used to handle user inputs and form submissions. It binds an object to a form and wires up validation using built-in or custom validation mechanisms. This means you can encapsulate model binding, submission, and validation in one compact UI component.
Here’s a basic example:
<EditForm Model="person" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" @bind-Value="person.Name" />
<ValidationMessage For="@(() => person.Name)" />
<button type="submit">Submit</button>
</EditForm>
Explanation:
Model
binds the form to an object (person
in this case).DataAnnotationsValidator
enables validation based on attributes like[Required]
,[StringLength]
,[Range]
, and more.ValidationSummary
shows all validation errors in a summary at the top of the form.ValidationMessage
displays a validation message next to the specific field it relates to.
This setup enables a declarative and streamlined approach to form handling.
How Does Validation Actually Work?
Blazor uses the EditContext
under the hood to manage form state and validation. Understanding this is key to implementing advanced logic.
Behind the scenes:
- When a user modifies a field,
EditContext
marks the field as “modified” and triggers field-level validation. - On form submission, Blazor performs model-wide validation, evaluating all rules from
DataAnnotations
or any custom validators. - If all validations pass,
OnValidSubmit
is invoked. Otherwise, the form renders the associated validation errors.
Here’s how you can tap into EditContext
and add logging or custom logic:
@code {
private EditContext editContext;
private Person person = new();
protected override void OnInitialized()
{
editContext = new EditContext(person);
editContext.OnValidationRequested += (s, e) => Console.WriteLine("Validation Requested");
editContext.OnFieldChanged += (s, e) => Console.WriteLine($"Field changed: {e.FieldIdentifier.FieldName}");
}
}
This gives full visibility into user interactions and validation triggers.
How to Add Custom Validation Logic
Blazor validation supports simple rules via annotations. But complex applications often require rules that go beyond that, such as conditional checks, multi-field consistency, or external API-based validations.
Option 1: Implement IValidatableObject
Implementing IValidatableObject
allows cross-field validation logic inside your model. It’s powerful for cases like ensuring password and confirm password fields match.
public class Person : IValidatableObject
{
public string Name { get; set; }
public string ConfirmName { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Name))
{
yield return new ValidationResult("Name is required.", new[] { nameof(Name) });
}
if (Name != ConfirmName)
{
yield return new ValidationResult("Name and confirmation must match.", new[] { nameof(ConfirmName) });
}
}
}
This method is great when business rules live within the domain model.
Option 2: Use a Custom Validator Component
For more dynamic scenarios, you can inject validation errors programmatically. This is helpful when validation depends on external services (like username availability).
public class CustomValidator : ComponentBase
{
[CascadingParameter] EditContext CurrentEditContext { get; set; }
private ValidationMessageStore messageStore;
protected override void OnInitialized()
{
messageStore = new ValidationMessageStore(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) =>
{
messageStore.Clear();
messageStore.Add(() => person.Name, "Custom name validation error.");
};
}
}
You can dynamically add/remove messages based on runtime logic.
Integrating FluentValidation
For a more fluent syntax and separation of concerns, FluentValidation
is a great choice. It supports complex rules, reusable validators, and localized messages.
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required.")
.MinimumLength(3).WithMessage("Name must be at least 3 characters long.");
RuleFor(x => x.Age)
.InclusiveBetween(18, 60).WithMessage("Age must be between 18 and 60.");
}
}
To integrate with Blazor, you’ll need to manually validate via dependency injection and handle error propagation through ValidationMessageStore
.
FAQ: Common Questions About Blazor Validation
Yes! By default, Blazor validates on submit. To validate on blur, handle the FieldChanged
event from the EditContext
and call NotifyFieldChanged()
.
Yes. Use ValidationMessageStore
to target individual fields and control message display precisely.
You can reset the validation state by calling:editContext.MarkAsUnmodified();
messageStore.Clear();
editContext.NotifyValidationStateChanged();
This clears messages and resets modified state, ideal after successful submission.
Conclusion: Make Blazor Forms Work for You
Blazor’s EditForm
gives you a solid foundation for form handling and validation. But knowing how to plug into the validation lifecycle unlocks serious flexibility. From IValidatableObject
to custom validators and full integration with FluentValidation
, you can build robust forms that scale with your app’s complexity.
Start small — inspect EditContext
, try a custom validation message — and scale up as your forms grow. Mastering this lets you build user-friendly forms that behave as expected and guide users effectively.
Want more deep dives like this? Drop a comment or check other posts in the Blazor series!