htmx Tutorial · Going Further · Lesson 28 of 30
Real-Time Updates: Polling, SSE and WebSockets
Keep a page up to date with polling, Server-Sent Events or WebSockets, and know which one to pick.
Polling: the simplest option
hx-trigger="every 5s" asks the server again and again. It works with any backend, through any proxy, and is easy to
scale. The cost is a request every few seconds even when nothing changed. To stop, have the server respond with
status 286, which tells htmx to stop polling that element.
<div hx-get="/job/42" hx-trigger="every 1s" hx-swap="innerHTML">Starting the export...</div>
<!-- The pretend server: on a real site this part is your backend (Python, PHP, Node, Go...) -->
<script>
let progress = 0;
server.get("/job/42", () => {
progress = Math.min(progress + 20, 100);
if (progress === 100) return { status: 286, body: "<strong>Export finished.</strong> (286: polling stopped)" };
return `Exporting... ${progress}%`;
});
</script>
Server-Sent Events (SSE)
For updates pushed by the server (notifications, live scores, progress), use the official SSE extension. The server keeps one HTTP response open and sends named events; each one is swapped in:
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div hx-ext="sse" sse-connect="/events">
<div sse-swap="notification">Waiting for notifications...</div>
</div>
The server sends lines like event: notification followed by data: <p>New message from Ada</p> and a blank line.
WebSockets
For two-way communication (chat, collaborative editing), the ws extension opens a WebSocket. Forms with
ws-send send their values as JSON over the socket, and HTML messages from the server are swapped by id (like
out-of-band swaps):
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div hx-ext="ws" ws-connect="/chat">
<div id="messages"></div>
<form ws-send><input name="message"></form>
</div>
Which one?
| Need | Use |
|---|---|
| Updates every few seconds or minutes are fine | Polling |
| The server pushes updates as they happen | SSE (one-way, plain HTTP, reconnects automatically) |
| Both sides send messages constantly | WebSockets |
Test yourself
How does a server tell htmx to stop polling an element?
Respond with status code 286Respond with 204Send HX-Stop: trueReturn an empty body
Show answer
Answer: Respond with status code 286
286 is htmx's special 'stop polling' status.