Blazorise Validation component

Help users identify and correct invalid form values.

Use validation components to check form input and show feedback beside the controls that need attention.

When validation is used together with a Field, Blazorise also updates accessibility attributes by default. Inputs can automatically render aria-invalid from the current validation status, while <FieldHelp> and validation messages are combined into aria-describedby so assistive technologies announce the same guidance users see on screen.

If you need custom associations, override them per component with AriaInvalid and AriaDescribedBy, or control the automatic behavior globally through BlazoriseOptions.AccessibilityOptions.

Structure

A Validations container groups individual Validation components and an optional summary. Each input can define messages in its Feedback fragment; TextInput is shown here as an example.

  • <Validations>Groups related validations.
    • <Validation>Applies validation rules to an input.
      • <TextInput>An example input component.
        • <Feedback>The input's named fragment for validation messages.
          • <ValidationSuccess>A message for a valid value.
          • <ValidationWarning>A message for a non-blocking warning.
          • <ValidationError>A message for an invalid value.
          • <ValidationNone>A message before validation has run.
          • <ValidationFeedback>An alternative that groups the feedback templates in one component.
    • <ValidationSummary>Lists the validation errors.

In most cases, you only need the <Validation> component together with <ValidationSuccess> and <ValidationError>. By default, validation runs automatically when an input value changes. Set the Validator handler to define the rules and return the validation result.

Examples

Method handlers

Method handlers are the easiest way to validate fields. Blazorise provides a set of predefined handlers through the ValidationRule helper class that you can assign to the Validator parameter.

In addition to the built-in handler methods, you can also create your own. For example, the following code snippet shows a custom ValidateEmail handler.

Please enter the name.
Name is good.
Please enter the email.
Email is good.
<Validation Validator="ValidationRule.IsNotEmpty">
    <Field>
        <TextInput Placeholder="Enter name">
            <Feedback>
                <ValidationNone>Please enter the name.</ValidationNone>
                <ValidationSuccess>Name is good.</ValidationSuccess>
                <ValidationError>Enter valid name!</ValidationError>
            </Feedback>
        </TextInput>
    </Field>
</Validation>

<Validation Validator="ValidateEmail">
    <Field>
        <TextInput Placeholder="Enter email">
            <Feedback>
                <ValidationNone>Please enter the email.</ValidationNone>
                <ValidationSuccess>Email is good.</ValidationSuccess>
                <ValidationError>Enter valid email!</ValidationError>
            </Feedback>
        </TextInput>
    </Field>
</Validation>
@code{
    void ValidateEmail( ValidatorEventArgs e )
    {
        var email = Convert.ToString( e.Value );

        e.Status = string.IsNullOrEmpty( email ) ? ValidationStatus.None :
            email.Contains( "@" ) ? ValidationStatus.Success : ValidationStatus.Error;
    }
}

Grouped feedbackAdded in v2.3

Use <ValidationWarning> when a value is acceptable but should still show non-blocking guidance. To keep all feedback fragments together, use <ValidationFeedback> with <None>, <Warning>, <Success>, and <Error> child fragments.

The same feedback structure applies to all Edit components (check, radio, select, etc.). Some components have special rules when defining child content and feedback.

Please enter a display name.
Short display names are allowed, but longer names are easier to recognize.
Display name looks good.
Please enter a username.
Username looks good.
Short usernames are allowed, but longer usernames are easier to identify.
<Validation Validator="@ValidateDisplayName">
    <Field>
        <TextInput Placeholder="Enter display name">
            <Feedback>
                <ValidationNone>Please enter a display name.</ValidationNone>
                <ValidationWarning>Short display names are allowed, but longer names are easier to recognize.</ValidationWarning>
                <ValidationSuccess>Display name looks good.</ValidationSuccess>
                <ValidationError>Display name is required.</ValidationError>
            </Feedback>
        </TextInput>
    </Field>
</Validation>

<Validation Validator="@ValidateUserName">
    <Field>
        <TextInput Placeholder="Enter username">
            <Feedback>
                <ValidationFeedback>
                    <None>Please enter a username.</None>
                    <Warning>Short usernames are allowed, but longer usernames are easier to identify.</Warning>
                    <Success>Username looks good.</Success>
                    <Error>Username is required.</Error>
                </ValidationFeedback>
            </Feedback>
        </TextInput>
    </Field>
</Validation>
@code{
    void ValidateDisplayName( ValidatorEventArgs e )
    {
        ValidateNameLength( e );
    }

    void ValidateUserName( ValidatorEventArgs e )
    {
        ValidateNameLength( e );
    }

    void ValidateNameLength( ValidatorEventArgs e )
    {
        var name = Convert.ToString( e.Value );

        e.Status = string.IsNullOrWhiteSpace( name )
            ? ValidationStatus.Error
            : name.Length < 4
                ? ValidationStatus.Warning
                : ValidationStatus.Success;
    }
}

AddonsAdded in v2.4

When validating an input inside <Addons>, place the <Validation> around the individual input and keep its messages in the input's <Feedback> content. The feedback is automatically rendered below the complete addon group so it does not disrupt the group layout or border radii.

This is a label
<Validation Validator="ValidationRule.IsNotEmpty">
    <Field>
        <Addons>
            <Addon AddonType="AddonType.Body">
                <TextInput Placeholder="Enter name">
                    <Feedback>
                        <ValidationNone />
                        <ValidationSuccess />
                        <ValidationError>Enter valid name!</ValidationError>
                    </Feedback>
                </TextInput>
            </Addon>
            <Addon AddonType="AddonType.End">
                <AddonLabel>This is a label</AddonLabel>
            </Addon>
        </Addons>
    </Field>
</Validation>

Data annotations

To use data annotations with Blazorise, you must combine both Validation and Validations components. The Validations component groups the fields used inside a Validation component. To make this work, you must meet two requirements:
  1. Validations component must reference the validated POCO through the Model parameter.
  2. Input component must bind to the model field through @bind-{Value} (e.g. @bind-Text).
After these requirements are met, Blazorise has enough information to apply data annotations.
@using System.ComponentModel.DataAnnotations

<Validations Mode="ValidationMode.Auto" Model="@user">
    <Validation>
        <Field Horizontal>
            <FieldLabel ColumnSize="ColumnSize.Is2">Full Name</FieldLabel>
            <FieldBody ColumnSize="ColumnSize.Is10">
                <TextInput Placeholder="First and last name" @bind-Value="@user.Name">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
    <Validation>
        <Field Horizontal>
            <FieldLabel ColumnSize="ColumnSize.Is2">Email</FieldLabel>
            <FieldBody ColumnSize="ColumnSize.Is10">
                <TextInput Placeholder="Enter email" @bind-Value="@user.Email">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
    <Validation>
        <Field Horizontal>
            <FieldLabel ColumnSize="ColumnSize.Is2">Password</FieldLabel>
            <FieldBody ColumnSize="ColumnSize.Is10">
                <TextInput Role="TextRole.Password" Placeholder="Password" @bind-Value="@user.Password">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
    <Validation>
        <Field Horizontal>
            <FieldLabel ColumnSize="ColumnSize.Is2">Re Password</FieldLabel>
            <FieldBody ColumnSize="ColumnSize.Is10">
                <TextInput Role="TextRole.Password" Placeholder="Retype password" @bind-Value="@user.ConfirmPassword">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
</Validations>
@code{
    User user = new User();

    public class User
    {
        [Required]
        [StringLength( 10, ErrorMessage = "Name is too long." )]
        public string Name { get; set; }

        [Required]
        [EmailAddress( ErrorMessage = "Invalid email." )]
        public string Email { get; set; }

        [Required( ErrorMessage = "Password is required" )]
        [StringLength( 8, ErrorMessage = "Must be between 5 and 8 characters", MinimumLength = 5 )]
        [DataType( DataType.Password )]
        public string Password { get; set; }

        [Required( ErrorMessage = "Confirm Password is required" )]
        [StringLength( 8, ErrorMessage = "Must be between 5 and 8 characters", MinimumLength = 5 )]
        [DataType( DataType.Password )]
        [Compare( "Password" )]
        public string ConfirmPassword { get; set; }

        [Required]
        public string Title { get; set; }

        [Range( typeof( bool ), "true", "true", ErrorMessage = "You gotta tick the box!" )]
        public bool TermsAndConditions { get; set; }
    }
}

Pattern validation

If you want to validate input using a regular expression instead of Validator handlers, use the Pattern parameter. Components that support the pattern attribute include TextInput, NumericInput and DateInput.
<Validation UsePattern>
    <Field>
        <TextInput Pattern="[A-Za-z]{3}">
            <Feedback>
                <ValidationError>Pattern does not match!</ValidationError>
            </Feedback>
        </TextInput>
    </Field>
</Validation>

Async validation

If you need to run validation using an external source or a REST API, Blazorise also supports async validation. The process is similar to a regular validator. You just need to define an awaitable handler using the AsyncValidator parameter.
@using System.Threading

<Validation AsyncValidator="@ValidateNameAsync">
    <Field>
        <TextInput Placeholder="Enter name">
            <Feedback>
                <ValidationError>Enter valid name!</ValidationError>
            </Feedback>
        </TextInput>
    </Field>
</Validation>
@code{
    Random random = new Random();

    async Task ValidateNameAsync( ValidatorEventArgs e, CancellationToken cancellationToken )
    {
        cancellationToken.ThrowIfCancellationRequested();

        // some long running task or call to the rest API
        await Task.Delay( random.Next( 1500 ) );

        e.Status = string.IsNullOrEmpty( Convert.ToString( e.Value ) )
            ? ValidationStatus.Error
            : ValidationStatus.Success;
    }
}

Manual validation

Sometimes you don't want to validate on every input change. In that case, use the <Validations> component to group multiple validations and then run validation manually.

In this example, the <Validations> component encloses multiple validation components and the Mode attribute is set to Manual. Validation is executed only when you click the submit button.
<Validations @ref="validations" Mode="ValidationMode.Manual">
    <Validation Validator="@ValidationRule.IsNotEmpty">
        <Field>
            <TextInput Placeholder="Enter first name" />
        </Field>
    </Validation>
    <Validation Validator="@ValidationRule.IsNotEmpty">
        <Field>
            <TextInput Placeholder="Enter last name" />
        </Field>
    </Validation>
    <Button Color="Color.Primary" Clicked="@Submit">Submit</Button>
</Validations>
@code{
    Validations validations;

    async Task Submit()
    {
        if ( await validations.ValidateAll() )
        {
            // do something
        }
    }
}

Localization

If you want to localize your validation messages, Blazorise provides an API and the required information needed for localization. This is done through the MessageLocalizer API. Before you use it, here's a quick breakdown of how it works.

A MessageLocalizer is straightforward. It accepts two parameters and returns a string. Its signature is: string Localize(string message, IEnumerable<string> arguments).

Where:

  • format raw validation message (format string)
  • arguments values used to populate the message

Now that you know what the API consists of, let's talk about the values it provides. The most important is the message parameter. Each message value is represented as a raw message in the form, before it is formatted.

For example, if you have a [Required] attribute set on your model field, this message will be "The {0} field is required.", and the arguments will contain the values needed to populate the placeholders inside of the message.

Example

For the basic example we're going to use MessageLocalizer directly on a Validation component.
@using Blazorise.Localization

<Validation MessageLocalizer="@Localize" />
@code{
    [Inject] ITextLocalizer<LocalizationValidationExample> L { get; set; }

    string Localize( string message, IEnumerable<string> arguments )
    {
        // You should probably do null checks here!
        return string.Format( L[message], arguments.ToArray() );
    }
}

Global Options

Setting the MessageLocalizer on each Validation is a good approach if you want per-component control. But a more practical way is to define it globally. If you remember from the Start Guide, we already have Validation defined in our application startup, so we just need to modify it a little.
services.AddBlazorise( options =>
    {
        options.ValidationMessageLocalizer = ( message, arguments ) =>
        {
            var stringLocalizer = options.Services.GetService<ITextLocalizer<YourResourceName>>();

            return stringLocalizer != null && arguments?.Count() > 0
                ? string.Format( stringLocalizer[message], arguments.ToArray() )
                : message;
        };
    } );

Validation summary

Sometimes you don't want to show error messages under each field. In those situations you can use the ValidationSummary component. Once placed inside of Validations it will show all error messages as a bullet list.

Note: The ValidationSummary component name conflicts with the built-in Microsoft.AspNetCore.Components.Forms.ValidationSummary from Blazor. If you are using both in the same project, reference Blazorise's version explicitly as <Blazorise.ValidationSummary>.

<Validations Mode="ValidationMode.Manual">
    <ValidationSummary Label="Following error occurs..." />

    @*other validation fields*@
</Validations>

Auto Validation

By default, the form is auto-validated on page load. To validate only after the user starts entering fields, set ValidateOnLoad to false.
<Validations Mode="ValidationMode.Auto" ValidateOnLoad>
    ...
</Validations>

Validation rules

Blazorise includes some predefined validation rules, e.g.:
<Validation Validator="@ValidationRule.IsNotEmpty">
    ...
</Validation>

IValidatableObject

This example demonstrates how to implement validation in a Blazor component using the IValidatableObject interface for custom logic. The form contains several fields bound to a CompanyInfo model, which is validated automatically using data annotations and custom validation logic.
@using System.ComponentModel.DataAnnotations
<Validations @ref="@validationsRef" Model="@Company" Mode="ValidationMode.Auto">
    <Validation>
        <Field>
            <FieldLabel>Name</FieldLabel>
            <FieldBody>
                <TextInput @bind-Value="@Company.Name">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
    <Validation>
        <Field>
            <FieldLabel>Description</FieldLabel>
            <FieldBody>
                <TextInput @bind-Value="@Company.Description">
                    <Feedback>
                        <ValidationError />
                    </Feedback>
                </TextInput>
            </FieldBody>
        </Field>
    </Validation>
    <Field>
        <Switch TValue="bool" Value="@Company.UseAlphaCode" ValueChanged="@OnUseAlphaCodeChanged" ValueExpression="@(() => Company.UseAlphaCode)">Use AlphaCode</Switch>
    </Field>
    <Fields>
        <Validation>
            <Field>
                <FieldLabel>AlphaCode</FieldLabel>
                <FieldBody>
                    <TextInput @bind-Value="@Company.AlphaCode">
                        <Feedback>
                            <ValidationError />
                        </Feedback>
                    </TextInput>
                </FieldBody>
            </Field>
        </Validation>
        <Validation>
            <Field>
                <FieldLabel>BetaCode</FieldLabel>
                <FieldBody>
                    <TextInput @bind-Value="@Company.BetaCode">
                        <Feedback>
                            <ValidationError />
                        </Feedback>
                    </TextInput>
                </FieldBody>
            </Field>
        </Validation>
    </Fields>
</Validations>
@code {
    async Task OnUseAlphaCodeChanged( bool value )
    {
        Company.UseAlphaCode = value;

        if ( validationsRef != null )
        {
            // retrigger validation for dependent properties
            await validationsRef.RetriggerValidation(
                () => Company.AlphaCode,
                () => Company.BetaCode );
        }
    }

    Validations validationsRef;

    CompanyInfo Company = new CompanyInfo()
    {
        UseAlphaCode = true,
    };

    public class CompanyInfo : IValidatableObject
    {
        [Required( ErrorMessage = "Name is required" )]
        public string Name { get; set; }

        [Required( ErrorMessage = "Description is required" )]
        public string Description { get; set; }

        public bool UseAlphaCode { get; set; }

        public string AlphaCode { get; set; }

        public string BetaCode { get; set; }

        [Range( 0, 999.99 )]
        public decimal Price { get; set; }

        public IEnumerable<ValidationResult> Validate( ValidationContext validationContext )
        {
            if ( UseAlphaCode )
            {
                if ( String.IsNullOrWhiteSpace( AlphaCode ) )
                {
                    yield return new ValidationResult( "AlphaCode is required", new[] { "AlphaCode" } );
                }
            }
            else
            {
                if ( String.IsNullOrWhiteSpace( BetaCode ) )
                {
                    yield return new ValidationResult( "BetaCode is required", new[] { "BetaCode" } );
                }
            }
        }
    }
}

List of the currently available validators.

Name
IsEmpty

Check if the string is null or empty.
IsNotEmpty

Check if the string is not null or empty.
IsEmail

Check if the string is an email.
IsAlpha

Check if the string contains only letters (a-zA-Z).
IsAlphanumeric

Check if the string contains only letters and numbers.
IsAlphanumericWithUnderscore

Check if the string contains only letters, numbers and underscore.
IsUppercase

Check if the string is uppercase.
IsLowercase

Check if the string is lowercase.
IsChecked

Checks if the boolean based input is checked.
IsSelected

Checks if the selection based input has a valid value selected. Valid values are anything except for null, string.Empty, or 0.
IsFileSelected

Checks if the file is selected.

Best Practices

Make Errors Actionable

Place feedback next to the related field and explain how to correct the value instead of merely stating that it is invalid. Validate after meaningful interaction or submission so users are not shown errors before they have had a chance to enter a value.

Validation Layers

Long forms benefit from a validation summary, but should retain field-level messages and move focus to a useful error location after submission. Repeat all security and business validation on the server because client-side validation improves feedback but does not establish trust.

API

See the API reference for the parameters, events, methods, and related types available to the components covered on this page.