Skip to content

Mutations return 204; SSE refreshes the lists

The conventional HTMX mutation returns the updated list partial for the client to swap in. We instead return 204 No Content with an HX-Trigger: closeDialog header, and rely on the SSE "items" channel to tell every connected client to re-fetch its list.

The app is used by two people on several devices at once, so a mutation has to reach screens other than the one that caused it regardless. Returning the partial as well would give the acting device a different, earlier refresh path than everyone else's — two code paths to keep in agreement, and a visible inconsistency whenever they drift.

Consequences

  • A mutation that fails to emit on the SSE channel leaves every client stale, including the one that performed it. The emit is not optional.
  • Validation failures are the exception: they re-render the sheet partial with status=400, which swaps into the open sheet.

Amendment (htmx 4 migration)

The decision above stands unchanged; one consequence no longer does.

Under HTMX 2 the 400 re-render needed help: HTMX skipped 4xx swaps by default, so frontend/main.js forced the swap in an htmx:beforeSwap handler. htmx 4 swaps every response except 204 and 304 (htmx.config.noSwap), so that handler was redundant and has been deleted. Nothing replaced it.

The 204 No Content + HX-Trigger: closeDialog half of the decision is untouched: htmx 4 still reads the response header, and it dispatches the event on the request's source element with bubbles, from a code path that runs even for a 204 — so it reaches the document-level listener from inside a <dialog> exactly as before.