Error Boundaries
Gracefully handle server-side exceptions with toasts or modals instead of broken UIs.
The Problem
When an HTMX request fails (e.g. throws a 500 Exception), ASP.NET Core usually renders a full HTML error page.
If the request was targeting a specific element (like hx-target="#panel"), this full HTML page gets inserted inside that element, breaking your layout.
SwapErrorBoundaries intercepts these exceptions and returns a graceful partial (like a Toast) instead.
🔒 Configuration
Enable it in your Program.cs:
builder.Services.AddSwapHtmx(options =>
{
// Enable HTMX specific error handling
options.ErrorHandling.Enabled = true;
// Optional: Show exception message in toast (useful for Dev)
options.ErrorHandling.ShowExceptionDetails = builder.Environment.IsDevelopment();
// Optional: Custom error view (default is "_SwapErrorToast")
options.ErrorHandling.ErrorViewName = "_MyErrorAlert";
});
🎨 Creating the Error Toast
Create a partial view Views/Shared/_SwapErrorToast.cshtml. The model is SwapErrorModel.
Use Out-Of-Band Swaps (hx-swap-oob) to ensure the toast appears in the correct place (e.g., a toast container at the bottom of the body), regardless of what the original hx-target was.
@using Swap.Htmx.Models
@model SwapErrorModel
<!--
"beforeend" inserts this toast at the end of #toast-container
See your Layout.cshtml for the container definition.
-->
<div id="toast-container" hx-swap-oob="beforeend">
<div class="toast error fade-in">
<div class="toast-header">
<strong>Error</strong>
<button onclick="this.closest('.toast').remove()">×</button>
</div>
<div class="toast-body">
@Model.Message
@if (Model.Exception != null)
{
<details>
<summary>Technical Details</summary>
<pre>@Model.Exception.GetType().Name</pre>
</details>
}
</div>
</div>
</div>
How It Works
- Interception:
SwapErrorMiddlewarecatches unhandled exceptions. - Detection: If it's an HTMX request, it suppresses the default error page.
- Response: It returns
200 OK(so HTMX processes the swap) but setsHX-Reswap: noneto prevent the main target from being overwritten. - Display: The OOB content in your partial view handles the display (e.g. appending to a toast list).