Form Validation
Lumeo's Form component
provides a structured way to build validated forms in Blazor. It wraps your form content in a
<form> element,
manages validation state through a cascading context, and fires separate callbacks for valid and invalid submissions.
Out of the box, Lumeo ships with a DataAnnotationsFormValidator
that works with standard .NET data annotations. You can also implement the
IFormValidator interface to plug in any validation strategy.
How it works
The form system is made up of several components that work together:
-
Form<TModel> — The top-level wrapper. Accepts a model, a validator, and submit callbacks. Cascades a
FormContextto all children. - FormField — Wraps a single field. Provides a label, help text, error display, and a required indicator. Supports vertical and horizontal orientations.
-
FormLabel — Renders a styled
<label>. Automatically turns red when its parent FormField has an error. - FormDescription — Displays muted helper text below the input.
- FormMessage — Displays the error message from the FormField context in red. Only renders when there is an error.
- FormItem — An optional grouping container that adds consistent spacing between label, input, and messages.
DataAnnotations validation
The simplest way to validate forms is with standard .NET data annotation attributes. Create a model class
with validation attributes, then pass a DataAnnotationsFormValidator
instance to the Form component.
1. Define the model
2. Build the form
3. Wire up the code
Form layout
The FormField component
supports two layout orientations: vertical (default) and horizontal. You can also control label width in horizontal mode.
Vertical layout (default)
Labels appear above the input. This is the default and works well for most forms.
Horizontal layout
Labels appear to the left of the input in a grid. Use LabelWidth to
control the label column width.
Using FormItem, FormLabel, FormDescription, and FormMessage
For more control over form field layout, you can compose the lower-level components directly
instead of relying on FormField.
FormLabel reads the
FormFieldContext from its
parent FormField and
automatically turns red when an error is present.
FormMessage renders the
error text only when the context has an error.
Validation styling
When a field has an error, several visual changes occur automatically:
-
Error message. The
FormFielddisplays a red error message below the input (or below the help text position).FormMessagealso renders the error from context. -
Label color. The
FormLabeltext turns to thetext-destructivecolor when its parent field has an error. - Help text hidden. When an error is present, the help text is hidden and replaced by the error message.
-
Required indicator. Fields with
Required="true"display a red asterisk next to the label.
All error colors use the --destructive
CSS variable, so they adapt to your theme automatically.
Async validation
For checks that have to round-trip the server — username availability, coupon codes, domain DNS — the
FormField component
accepts an AsyncValidator:
a Func<string?, Task<string?>>
returning an error message (or null
on success). It runs alongside the synchronous validator and exposes its in-flight state through
IsValidating on the
field and IsAnyFieldValidating
on the surrounding FormContext
— disable the submit button on the latter so users can't race a stale check.
Configure the debounce with AsyncValidationDebounceMs
(default 300) and the trigger with AsyncValidateOn
(OnChange or
OnBlur).
See the FormField "Async validators" section for the full parameter list and a spinner / submit-disable example.
FormContext
The Form component cascades
a FormContext object to all
child components. This context tracks errors, dirty fields, and submission state.
| Member | Type | Description |
|---|---|---|
| Errors | Dictionary<string, List<string>> | All current validation errors keyed by field name. |
| DirtyFields | HashSet<string> | Names of fields that have been modified. |
| IsSubmitting | bool | True while the submit handler is executing. |
| IsValid | bool | True when there are no validation errors. |
| GetFieldErrors(field) | List<string> | Returns all error messages for a specific field. |
| HasError(field) | bool | Returns true if a specific field has errors. |
| IsDirty(field) | bool | Returns true if a field has been modified. |
| MarkDirty(field) | void | Marks a field as dirty (modified). |
| ClearErrors() | void | Removes all validation errors. |
| Reset() | void | Clears all errors, dirty fields, and submission state. |
Custom validation
For validation logic beyond data annotations, implement the
IFormValidator interface.
This gives you full control over how validation is performed.
The IFormValidator interface
Example: custom validator
Using the custom validator
Complete example
Here is a full registration form that uses multiple Lumeo form components together with DataAnnotations validation.
Model
Razor markup
Code-behind
API reference
Form<TModel>
| Parameter | Type | Default | Description |
|---|---|---|---|
| Model | TModel? | null | The form model instance to validate. |
| Validator | IFormValidator? | null | The validator to run on submit. |
| OnValidSubmit | EventCallback<TModel> | — | Called when the form is submitted and validation passes. |
| OnInvalidSubmit | EventCallback<TModel> | — | Called when the form is submitted and validation fails. |
| ChildContent | RenderFragment? | null | The form content. |
| Class | string? | null | Additional CSS classes. |
FormField
| Parameter | Type | Default | Description |
|---|---|---|---|
| Label | string? | null | The field label text. |
| HelpText | string? | null | Descriptive text shown below the input. Hidden when an error is present. |
| Error | string? | null | Error message to display. When set, replaces help text. |
| Required | bool | false | Shows a red asterisk next to the label. |
| Name | string? | null | Field name for context identification. |
| Orientation | Lumeo.Orientation | Vertical | Layout direction: Vertical or Horizontal. |
| LabelWidth | string? | null | Label column width in horizontal mode (e.g. "120px"). |
| Class | string? | null | Additional CSS classes. |