Swap.Htmx 0.3.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Swap.Htmx --version 0.3.2
                    
NuGet\Install-Package Swap.Htmx -Version 0.3.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Swap.Htmx" Version="0.3.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Swap.Htmx" Version="0.3.2" />
                    
Directory.Packages.props
<PackageReference Include="Swap.Htmx" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Swap.Htmx --version 0.3.2
                    
#r "nuget: Swap.Htmx, 0.3.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Swap.Htmx@0.3.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Swap.Htmx&version=0.3.2
                    
Install as a Cake Addin
#tool nuget:?package=Swap.Htmx&version=0.3.2
                    
Install as a Cake Tool

Swap.Htmx

NuGet License: MIT

Minimal HTMX framework for ASP.NET Core MVC that provides automatic page/partial detection, toast notifications, out-of-band swaps, and a powerful event system for decoupling domain logic from UI updates.

Features

  • SwapController - Automatic page vs partial rendering based on HX-Request header
  • Toast Notifications - Built-in success/error/warning/info toasts with zero JavaScript
  • Out-of-Band Swaps - Update multiple page sections in one response
  • Event System - Chain domain events to UI updates with static typing
  • Middleware - Validates responses and headers automatically
  • Extension Methods - Fluent API for HTMX headers and responses

Installation

dotnet add package Swap.Htmx

📖 Complete Setup Guide - Step-by-step instructions for setting up toasts, OOB swaps, and all features

Quick Start

1. Register Services & Middleware

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();
builder.Services.AddSwapHtmx();

var app = builder.Build();

app.UseSwapHtmxShell(); // Validates HTMX responses
app.UseSwapHtmx();      // Adds event handling middleware

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

2. Create Controller

public class ProductController : SwapController
{
    public async Task<IActionResult> Index()
    {
        var products = await _service.GetAllAsync();
        return SwapView(products); // Auto-detects page vs partial
    }
    
    public async Task<IActionResult> Create(ProductDto dto)
    {
        await _service.CreateAsync(dto);
        
        // Show success toast
        Response.ShowSuccessToast("Product created!");
        
        return SwapView("Success");
    }
}

3. Create View

@model List<Product>

<div id="product-list">
    <h1>Products</h1>
    
    @foreach (var product in Model)
    {
        <div class="product-card">
            <h3>@product.Name</h3>
            <p>$@product.Price</p>
            
            <button hx-post="/products/delete/@product.Id" 
                    hx-target="#product-list"
                    hx-confirm="Delete this product?">
                Delete
            </button>
        </div>
    }
</div>

Core Concepts

SwapView() - Automatic Rendering

SwapView() automatically returns the correct response type:

public async Task<IActionResult> Details(int id)
{
    var product = await _service.GetAsync(id);
    
    // Initial page load: Returns View() with layout
    // HTMX request: Returns PartialView() without layout
    return SwapView("Details", product);
}

How it works:

  • Checks for HX-Request header
  • HTMX request → PartialView() (no layout)
  • Normal request → View() (with layout)
  • Adds Vary: HX-Request header for caching

Toast Notifications

Show user feedback with simple extension methods:

Response.ShowSuccessToast("Product saved!");
Response.ShowErrorToast("Something went wrong!");
Response.ShowWarningToast("Please review your changes.");
Response.ShowInfoToast("Processing in background...");

Features:

  • 4 toast types with different colors
  • Auto-dismiss after 3 seconds
  • Configurable positioning (top-right, bottom-right, etc.)
  • Multiple toasts stack vertically
  • Pure HTMX - no JavaScript required

📖 Full Toast Documentation

Out-of-Band (OOB) Swaps

Update multiple page sections in a single response:

public async Task<IActionResult> AddToCart(int productId)
{
    await _cartService.AddItemAsync(productId);
    
    // Main content
    var main = SwapView("ItemAdded");
    
    // Also update cart total in header (out-of-band)
    var total = await _cartService.GetTotalAsync();
    ViewData["OobCartTotal"] = $@"
        <div id=""cart-total"" hx-swap-oob=""true"">
            {total.ItemCount} items - ${total.Total}
        </div>";
    
    return main;
}

Common use cases:

  • Update header badge counts
  • Refresh sidebar panels
  • Update multiple dashboard widgets
  • Sync item in list after editing details

📖 Full OOB Swap Documentation

Event System

Chain domain events to UI updates without coupling:

// Define event keys (static typing enforced)
public static class ProductEvents
{
    public static readonly EventKey Created = new("product.created");
    public static readonly EventKey Updated = new("product.updated");
}

public static class UiEvents
{
    public static readonly EventKey RefreshList = new("ui.refreshList");
    public static readonly EventKey ShowToast = new("ui.toast.success");
}

// Configure event chains
builder.Services.AddSwapHtmx(events =>
{
    // When product is created, refresh list and show toast
    events.Chain(ProductEvents.Created, 
                 UiEvents.RefreshList, 
                 UiEvents.ShowToast);
});

// In controller, emit domain event
public async Task<IActionResult> Create(ProductDto dto)
{
    await _service.CreateAsync(dto);
    
    // Emit domain event (triggers UI events via chain)
    await _publisher.EmitAsync(ProductEvents.Created);
    
    return SwapView("Success");
}

📖 Full Event System Documentation

Extension Methods

Request Extensions

if (Request.IsHtmxRequest())
{
    // Handle HTMX request
}

if (Request.IsBoosted())
{
    // Handle boosted request
}

var currentUrl = Request.GetCurrentUrl();
var target = Request.GetHtmxTarget();

Response Extensions

// Set HX-Redirect
Response.HxRedirect("/products");

// Set HX-Refresh
Response.HxRefresh();

// Set HX-Location with context
Response.HxLocation("/products/details/1", new { target = "#main" });

// Trigger client-side events
Response.HxTrigger("productUpdated");
Response.HxTrigger(new { showModal = new { id = 123 } });

// Set HX-Retarget
Response.HxRetarget("#different-element");

// Set HX-Reswap
Response.HxReswap("outerHTML");

Testing

The framework includes comprehensive test coverage:

  • 38 Unit Tests - Verify methods, headers, event chains
  • 16 E2E Tests - Playwright tests in real browsers
    • 6 toast tests
    • 5 OOB swap tests
    • 4 combined feature tests
    • 1 debug test
# Run unit tests
cd framework/Swap.Htmx.Tests
dotnet test

# Run E2E tests (requires test app running)
cd framework/Swap.Htmx.E2ETests
dotnet test

Documentation

Getting Started

Features

Reference

Examples

See the test app for complete working examples:

cd framework/Swap.Htmx.TestApp/src
dotnet run
# Visit http://localhost:5000/test

Philosophy

Swap.Htmx is minimal by design:

  1. Automatic View Rendering - SwapView() handles page vs partial logic
  2. Domain→UI Event Mapping - Emit domain events, UI updates follow
  3. HTMX-Native - Leverage HTMX's capabilities, don't fight them
  4. Static Typing - No magic strings for event names

Everything else is just sensible defaults and extension methods.

Requirements

  • .NET 8.0 or higher
  • ASP.NET Core MVC
  • HTMX 2.0+ (via CDN or npm)

License

MIT License - see LICENSE file for details.

Contributing

See CONTRIBUTING.md for guidelines.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.6 50 12/11/2025
1.0.5 56 12/11/2025
1.0.4 79 12/10/2025
1.0.3 73 12/9/2025
1.0.2 193 12/5/2025
1.0.1 680 12/2/2025
1.0.0 521 11/27/2025
0.14.0 164 11/26/2025
0.13.0 163 11/26/2025
0.12.0 166 11/26/2025
0.11.4 170 11/25/2025
0.11.3 175 11/24/2025
0.11.2 344 11/21/2025
0.11.1 275 11/21/2025
0.11.0 300 11/21/2025
0.10.0 341 11/21/2025
0.9.1 385 11/20/2025
0.9.0 379 11/20/2025
0.8.2 386 11/20/2025
0.8.1 388 11/20/2025
0.8.0 379 11/20/2025
0.7.1 381 11/20/2025
0.7.0 385 11/20/2025
0.6.0 386 11/20/2025
0.5.1 384 11/19/2025
0.5.0 317 11/17/2025
0.4.1 321 11/17/2025
0.4.0 266 11/16/2025
0.3.5 242 11/14/2025
0.3.4 281 11/12/2025
0.3.3 264 11/12/2025
0.3.2 275 11/11/2025
0.3.1 189 11/6/2025
0.3.0 190 11/3/2025
0.2.0-dev 120 11/1/2025
0.1.0 177 10/30/2025