Move SSE out of htmx core into a standalone extension (hx-sse.js).
The new extension uses Fetch + ReadableStream instead of EventSource,
so it works with POST, custom headers, and cookies. Two modes:
- One-off streams: any hx-get/hx-post returning text/event-stream
- Persistent connections: hx-sse:connect with auto-reconnect
SSE events now follow the extension pattern:
- htmx:before:sse:connection / htmx:after:sse:connection
- htmx:before:sse:message / htmx:after:sse:message
- htmx:sse:error / htmx:sse:close
New features:
- hx-sse:close="eventname" to close connection on a named event
- htmx:sse:close event with reason (message, removed, ended, etc.)
- htmx:before:response core event for extension response interception
- Server retry: field updates reconnect delay per SSE spec
- Non-2xx reconnect responses fire htmx:sse:error
SSE parser improvements (spec compliance):
- Handle CRLF, CR, and LF line endings
- Strip leading BOM (U+FEFF)
- Dispatch empty data fields
- Ignore id fields containing NULL
- Process field-only lines (no colon) per spec
Docs:
- Document htmx:before:response in events, reference, building guide
- Add htmx 2.x → 4.x SSE migration guide
- Document hx-sse:close and htmx:sse:close
- Add hx-sse:connect, hx-sse:close to JetBrains web-types
- Fix stale Accept header reference in migration guide
Remove htmx.config.sse from core config tables (extension-only now).
Remove stale SSE event references from core docs.
* remove apline attributes from pre settle nodes
* simplify a little
* move to an extension
* Try applying alpine to fragments before swap
* handle morph properly
* defer count tracking
* handle child template morph and use local alpine for test reliablilty
* improve defercount
* post rebase tidy
* improve x-id and x-for handling
* patch isSoftMatch instead of removing generated id's
* feat(ws): handle non-JSON messages as raw HTML with cancelable event
Non-JSON WebSocket messages now swap as raw HTML instead of being
dropped. Fires a cancelable htmx:ws:rawMessage event before swapping,
allowing custom handling when needed.
- If hx-target is set: swaps into target using element's swap style
- If no hx-target: uses swap:none to protect connection element,
but hx-partial tags in payload still reach their own targets
- preventDefault() on the event skips default swap for custom handling
Replaces the old htmx:wsUnknownMessage event which only notified
but took no action on non-JSON data.
* test(ws): add tests for raw HTML message handling and cancelable event
Tests cover:
- Non-JSON messages swap as raw HTML into hx-target
- swap:none used when no hx-target (protects connection element)
- hx-partial tags in raw messages still reach their targets
- htmx:ws:rawMessage event fires with message data
- preventDefault() on rawMessage cancels default swap
---------
Co-authored-by: Stu Kennedy <stu@stukennedy.com>
* Close SSE stream on visibility change to fix iOS Safari background disconnect (#3634)
iOS Safari silently kills TCP connections when the app is backgrounded,
leaving SSE streams dead with no error event. The new `closeOnHide`
config option proactively cancels the stream reader when the page
becomes hidden and reconnects when visible again (requires
`reconnect: true`).
Also renames `pauseInBackground` to `closeOnHide` across source, docs,
types, and tests to reflect the new disconnect/reconnect behavior
(previously it only paused processing).
* Improve SSE closeOnHide with reconnectRequested flag and cancellable backoff
- Create reader outside __parseSSE and pass as argument
- Add reconnectRequested flag so visibility reconnects skip backoff delay
- Symmetric visibility handler: cancel on hidden, reconnect on visible
- Cancellable backoff delay via delayCanceller so tab return is instant
* add upsert swap extension
* improve upsert
* simplify upsert to not use morph
* add doco
* Add hx-upsert tag support as well
---------
Co-authored-by: MichaelWest22 <michael.west@docuvera.com>
* handle newContent processing in other swap styles
* Handle outerMorph newContent processing by adding target to newContent so it will be processed as well. also simplified textContent swaps
---------
Co-authored-by: MichaelWest22 <michael.west@docuvera.com>
* refactor: Enhance WebSocket extension with URL normalization, improved request management, and refined message handling for better reliability and clarity.
feat: Add manual WebSocket server script and enhance WebSocket documentation with detailed message formats and connection management improvements.
feat: Include event type in WebSocket messages and update documentation for message format
* refactor: Update WebSocket extension to connect immediately by default, enhance documentation on connection triggers, and improve message handling examples.
* feat: Introduce URL validation for WebSocket send attributes to ensure proper connection handling and prevent non-URL markers from being processed.
* change thisElt to thisAttr to simplify
* Handle returning this properlty at any level
* add this wrapper for clarity
* move to callback instead
* rename to eltCollector
---------
Co-authored-by: MichaelWest22 <michael.west@docuvera.com>
* handle transition overrides on oob/partial
* Handle swap dealys properly for transition true/false and respect main swap transition and swap delay.
* swapDelay let
---------
Co-authored-by: MichaelWest22 <michael.west@docuvera.com>