SSE dan WebSocket — HTMX

HTMX mendukung Server-Sent Events dan WebSocket untuk real-time updates. SSE (Server-Sent Events) Waiting for messages... WebSocket Kirim Server (Laravel SSE)…

Polling bertanya berulang kali "ada yang baru?". SSE (Server-Sent Events) membalik arahnya: browser membuka satu koneksi yang dibiarkan terbuka, dan server mendorong kabar lewat koneksi itu begitu ada perubahan. WebSocket juga satu koneksi terbuka, tapi dua arah: browser dan server sama-sama bisa mengirim kapan saja. Bayangin pelanggan Laundry Bu Rini. Polling itu pelanggan yang menelepon tiap 30 menit, "cucian saya sudah?". SSE itu pelanggan yang berlangganan pesan dari Bu Rini, dan pesannya datang tepat saat statusnya berubah. WebSocket itu telepon yang dibiarkan tersambung, dan dua-duanya bisa bicara.

🧪 Tebakan cepat: Papan memakai hx-trigger="every 30s", dibuka pukul 08.00.00. Bu Rini mengubah #12 jadi Disetrika pukul 08.00.04. Kapan papan menampilkannya? Dan kalau papannya memakai SSE? Jawabannya ada di bagian "Polling atau SSE: kapan kabarnya sampai".

Aliran SSE: satu koneksi, banyak pesan

Browser meminta GET /papan/stream dan server membalas dengan Content-Type: text/event-stream tanpa menutup koneksinya. Setiap ada perubahan, server menulis satu pesan:

id: 1
event: order-12
data: <li id="order-12" sse-swap="order-12" hx-swap="outerHTML">#12 Pak Darto, 3 kg: <span>Disetrika</span></li>

id: 2
event: orderBaru
data: <li id="order-15" sse-swap="order-15" hx-swap="outerHTML">#15 Bu Tini, 4 kg: <span>Antre</span></li>

Extension sse: sse-connect dan sse-swap

<script src="https://unpkg.com/[email protected]"></script>

<div hx-ext="sse" sse-connect="/papan/stream">
  <ul id="papan-order" sse-swap="orderBaru" hx-swap="beforeend">
    <li id="order-12" sse-swap="order-12" hx-swap="outerHTML">#12 Pak Darto, 3 kg: <span>Dicuci</span></li>
    <li id="order-13" sse-swap="order-13" hx-swap="outerHTML">#13 Mbak Wulan, 2 kg: <span>Disetrika</span></li>
  </ul>
  <span hx-get="/jumlah" hx-trigger="sse:orderBaru">3 order hari ini</span>
</div>

Di Laravel 13, response()->eventStream() menulis format itu untukmu:

use Illuminate\Http\StreamedEvent;
use Illuminate\Support\Str;

Route::get('/papan/stream', function () {
    return response()->eventStream(function () {
        $sejak = now();
        while (true) {
            $cek = now();
            foreach (Order::where('updated_at', '>', $sejak)->get() as $order) {
                $baris = view('papan._baris', ['order' => $order])->render();
                yield new StreamedEvent(event: "order-{$order->id}", data: Str::squish($baris));
            }
            $sejak = $cek;
            sleep(1);
        }
    });
});

Str::squish() meratakan HTML jadi satu baris. Nilai yang di-yield tanpa StreamedEvent diberi nama update, bukan message.

Polling atau SSE: kapan kabarnya sampai

every 30s SSE
#12 berubah 08.00.04, tampil di papan 08.00.30, telat 26 detik 08.00.04, langsung
Request per jam, satu tab 120, kebanyakan isinya sama 1 koneksi yang terus terbuka

Polling menampilkannya pukul 08.00.30, pas giliran bertanya berikutnya. SSE menampilkannya saat itu juga. Itu jawaban tebakannya.

Kalau koneksinya putus

WebSocket: dua arah

<script src="https://unpkg.com/[email protected]"></script>

<div hx-ext="ws" ws-connect="/papan/ws">
  <ul id="pesan"></ul>
  <form ws-send>
    <input name="pesan" placeholder="Pesan ke kurir">
    <button>Kirim</button>
  </form>
</div>

Kapan polling sudah cukup

⚠️ Jebakan yang sering ketemu

TL;DR: SSE (Server-Sent Events) adalah satu koneksi satu arah, dari server ke browser, dengan pesan event:, data:, dan id: dipisah baris kosong. WebSocket dua arah. Dengan extension sse: hx-ext="sse" dan sse-connect="/papan/stream" di pembungkus, sse-swap="order-12" (atau message untuk pesan tanpa nama) di elemen penerima, hx-trigger="sse:orderBaru" untuk memicu request, dan sse-close untuk menutup. Putus sebentar: browser menyambung ulang dan mengirim Last-Event-ID. Browser menyerah: extension mencoba lagi dengan jeda 0,5, 1, 2, 4 detik sampai 64 detik. Dengan extension ws: ws-connect, pesan HTML ditukar menurut id, dan ws-send mengirim form sebagai JSON. every 30s bisa telat sampai 30 detik, SSE langsung. Polling cukup kalau telat gak masalah, dan ingat SSE memegang satu worker PHP per tab.

Yang akan kamu pelajari