Real-Time Updates (SSE)

Push updates to the client without page reloads using Server-Sent Events.

Overview

Swap.Htmx provides a first-class integration with Server-Sent Events (SSE). This allows you to push HTML updates to the client for features like live dashboards, notifications, or chat.


Setup

1. Install Package

The core Swap.Htmx package includes basic SSE support, but Swap.Htmx.Realtime adds the event bridge.

dotnet add package Swap.Htmx.Realtime

2. Configure Services

In Program.cs:

builder.Services.AddSwapHtmx()
    .AddSseEventBridge(); // Enable the bridge

// ...

app.UseSwapHtmx();
app.UseSseEventBridge(); // Add middleware

Creating the Stream

1. The Controller

Inherit from SwapRealtimeController to get access to the ServerSentEvents helper.

using Swap.Htmx;
using Swap.Htmx.Realtime;

public class DashboardController : SwapRealtimeController
{
    // The endpoint the browser connects to
    [HttpGet("/stream")]
    public IActionResult Stream()
    {
        return ServerSentEvents(async (connection, ct) => 
        {
            // Subscribe this connection to specific channels
            connection.Subscribe("dashboard-updates");
            
            // Keep connection open
            await connection.KeepAlive(TimeSpan.FromSeconds(30), ct);
        });
    }
}

2. The View

Use the standard HTMX SSE extension.

<!-- Connect to the stream -->
<div hx-ext="sse" sse-connect="/stream">

    <!-- When the 'stats' event arrives, swap this element's content -->
    <div sse-swap="stats">
        Processing...
    </div>

</div>

Triggering Updates

You can broadcast updates from anywhere in your application (Background Service, Controller, MediatR handler).

public class Worker : BackgroundService
{
    private readonly ISsseEventService _sse;

    public Worker(ISsseEventService sse)
    {
        _sse = sse;
    }

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            // Broadcast HTML to all subscribers
            await _sse.BroadcastAsync(
                channel: "dashboard-updates",
                eventName: "stats",
                html: "<div>CPU Usage: 45%</div>"
            );
            
            await Task.Delay(1000);
        }
    }
}