Validation
Handling form validation errors with HTMX partials.
The HTMX Validation Pattern
In a standard MVC app, validation errors often require a full page reload to display asp-validation-sumary. With Swap.Htmx, we can return just the form (with errors) while keeping the rest of the page state intact.
1. The Controller Pattern
The standard pattern is:
- Check
ModelState.IsValid. - If Invalid: Return the Form Partial (with the model).
- If Valid: Process and return success (or a different partial).
[HttpPost]
public IActionResult Save(ProductModel model)
{
if (!ModelState.IsValid)
{
// ❌ Return the form partial with validation errors
// The URL will not change, preserving the user's context.
return SwapView("_CreateForm", model);
}
_service.Save(model);
// ✅ Success: Replace the form with a success message
// OR: Close the modal
return SwapView("_SuccessMessage", model);
}
2. The View (_CreateForm.cshtml)
Use standard ASP.NET Core Validation Helpers. They work perfectly with HTMX.
@model ProductModel
<!--
hx-post: Posts to the controller
hx-target: "this" replaces the form itself with the response
(which will be the form again if errors exist)
-->
<form hx-post="@Url.Action("Save")" hx-target="this" hx-swap="outerHTML">
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
<!-- Standard validation message -->
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<button type="submit">Save</button>
</form>
Client-Side vs Server-Side
While HTMX is server-driven, you can still use jquery-validation-unobtrusive for instant client-side feedback.
However, relying on Server-Side validation is often simpler and more robust, as you don't need to duplicate validation logic in JavaScript. Since HTMX requests are lightweight, the "round trip" to validate on the server is usually imperceptible to the user.