Client-side validation runs in the browser using jQuery Validation and jQuery Unobtrusive Validation. It reads the data-val-* attributes generated by Tag Helpers and DataAnnotations, validates field values before the form is submitted, and shows error messages inline — without a round-trip to the server. This improves perceived responsiveness: validation errors appear instantly as the user fills the form. Importantly, client-side validation is a convenience feature, not a security measure — it can always be bypassed. Server-side validation is the authoritative check; client-side is a UX enhancement.
Setting Up Client-Side Validation
@* ── Include validation scripts (in the Scripts section of the view) ─────── *@
@section Scripts {
@* _ValidationScriptsPartial includes jquery.validate.min.js
and jquery.validate.unobtrusive.min.js *@
<partial name="_ValidationScriptsPartial" />
}
@* ── What the Tag Helper generates for a validated field ────────────────── *@
@* Input in the view: *@
<input asp-for="Title" class="form-control" />
@* Generated HTML — data-val-* attributes drive client-side validation: *@
<input type="text"
id="Title"
name="Title"
value=""
class="form-control"
data-val="true"
data-val-required="Title is required."
data-val-length="Title must be between 5 and 200 characters."
data-val-length-min="5"
data-val-length-max="200" />
@* ── The corresponding error span ────────────────────────────────────────── *@
<span asp-validation-for="Title"
class="text-danger field-validation-valid">
@* filled by jquery.validate.unobtrusive when validation fails *@
</span>
_ValidationScriptsPartial.cshtml bundled with the MVC template references jQuery Validation and jQuery Unobtrusive Validation from the wwwroot/lib/ folder. If these libraries are not present (e.g., after running npm install or LibMan to manage client-side libraries), client-side validation silently stops working — forms submit without client-side checks and only server-side validation catches errors. Run libman restore or check wwwroot/lib/jquery-validate/ exists after cloning the repository.$.validator.unobtrusive.parse(formElement) after injecting the form into the DOM. jQuery Unobtrusive Validation only attaches itself to forms present at DOMContentLoaded — forms injected later (modal dialogs, tab panels loaded via AJAX) need to be explicitly parsed. Forgetting this is the most common cause of “client-side validation not working in a modal form.”data-val attributes, using Postman or curl to submit directly to the endpoint, or using a malicious browser extension. Always treat client-side validation as a user experience enhancement only. Every form submission must have server-side validation as the authoritative check. Never rely on client-side validation for security decisions.Custom Client-Side Validator
// ── To add client-side support for a custom ValidationAttribute, ──────────
// implement IClientModelValidator on the attribute:
public sealed class SlugFormatAttribute : ValidationAttribute, IClientModelValidator
{
// ... server-side IsValid implementation (from previous lesson) ...
// Client-side: adds data-val-* attributes that jQuery Validate reads
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes["data-val"] = "true";
context.Attributes["data-val-slugformat"] = FormatErrorMessage(context.ModelMetadata.DisplayName);
}
}
// ── Register the custom jQuery validator (in site.js or a dedicated script) ─
@* In the Scripts section of any view using SlugFormat: *@
<script>
// Register the custom validator method
$.validator.addMethod("slugformat", function (value, element) {
return this.optional(element) || /^[a-z0-9-]+$/.test(value);
});
// Register the unobtrusive adapter to connect data-val-slugformat to the method
$.validator.unobtrusive.adapters.addBool("slugformat");
</script>
Common Mistakes
Mistake 1 — Not including _ValidationScriptsPartial (client validation silently disabled)
❌ Wrong — validation scripts not loaded; forms submit without client checks:
@section Scripts { } @* empty — no validation scripts loaded *@
✅ Correct — always include <partial name="_ValidationScriptsPartial" /> in the Scripts section of forms.
Mistake 2 — Trusting client-side validation for security decisions
❌ Wrong — skipping server-side validation because “the client already validated it.”
✅ Correct — always check ModelState.IsValid server-side regardless of client-side state.