#
Delete Row
#
Implementing Row Deletion with htmx and ASP.NET Core
The Delete Row pattern is a common requirement in data-driven applications. Using htmx, you can remove a record from the database and concurrently update the UI by removing the corresponding row from a table—all without a full page reload.
#
The Frontend: Razor & htmx
In Index.cshtml, we set up a table where the <tbody> is configured to handle the deletion logic for all its child rows.
Index.cshtml
<tbody hx-confirm="Are you sure?" hx-target="closest tr" hx-swap="outerHTML swap:1s">
@foreach (var contact in Model.Contacts)
{
<partial name="_TableRow" model="@contact"/>
}
</tbody>
The individual rows are rendered via a partial view. Each row contains a "Delete" button that triggers the htmx request.
_TableRow.cshtml
@model Contact
<tr>
<td>@Model.Name</td>
<td>@Model.Email</td>
<td>@Model.Status</td>
<td>
<button class="btn btn-danger"
hx-post="@Url.Page("Index", "Contact", new { Id = Model.Id })"
hx-include="closest form">
Delete
</button>
</td>
</tr>
Key htmx attributes used:
hx-confirm: Automatically triggers a browser confirmation dialog before the request is sent.hx-post: Sends a POST request to theContacthandler with the specific contact's ID.hx-target="closest tr": (Defined on the parenttbody) Tells htmx to target the table row containing the clicked button.hx-swap="outerHTML swap:1s": Replaces the entire row with the server response (which is empty in this case). Theswap:1sallows for a CSS transition effect (like a fade-out) before the element is finally removed from the DOM.hx-include="closest form": Ensures the Anti-Forgery Token from the form is included in the POST request.
#
The Backend: C# PageModel
The IndexModel handles the deletion request. When the record is successfully deleted from the service, it returns an OkResult. Since the response body is empty, htmx simply removes the target element (the row) from the page.
Index.cshtml.cs
[ValidateAntiForgeryToken]
public class IndexModel(IContactService contactService) : PageModel
{
public IList<Contact>? Contacts { get; set; }
[FromQuery(Name = "Id")] public int Id { get; set; }
public void OnGet()
{
this.Contacts = contactService.Get().ToArray();
}
public IActionResult OnPostContact()
{
// Delete the contact from the data store
contactService.Delete(this.Id);
// Returning an empty OkResult tells htmx to
// remove the target element from the DOM.
return new OkResult();
}
}
#
Why this works well
- Declarative UI: You define the confirmation and targeting logic once on the container (
<tbody>), making the individual row templates cleaner. - Visual Feedback: By using
swap:1s, you can use CSS transitions to provide a smooth removal animation, making the UI feel more polished. - Low Overhead: The server only needs to return a simple
200 OKstatus, minimizing bandwidth and processing time. - Security: The use of
[ValidateAntiForgeryToken]andhx-include="closest form"ensures that the deletion requests are protected against CSRF attacks.