htmx adalah library JavaScript kecil yang membuat elemen HTML bisa bertanya ke server sendiri. Kamu cukup menambah atribut berawalan hx- di tag-nya: elemen itu mengirim request (permintaan dari browser ke server), server membalas dengan potongan HTML, lalu htmx menaruh potongan itu di bagian halaman yang kamu tunjuk. Bagian lain gak disentuh, dan halamannya gak di-reload. Bayangin papan order di meja Laundry Bu Rini. Waktu cucian #12 pindah dari "Dicuci" ke "Disetrika", Bu Rini gak menghapus lalu menulis ulang seluruh papan. Dia menghapus satu kotak status, lalu menulis yang baru. Link biasa itu menulis ulang seluruh papan. htmx cuma mengganti satu kotak itu.
🧪 Tebakan cepat: Tombol "Cek status" di papan order Bu Rini mengambil status terbaru dari server dan menaruhnya di halaman tanpa reload. Berapa baris JavaScript yang harus kamu tulis sendiri untuk itu? Jawabannya ada di bagian "Apa yang terjadi saat tombolnya diklik".
Pasang htmx dengan satu tag script
<script src="https://unpkg.com/[email protected]"></script>
unpkg.comadalah CDN: server yang membagikan file library dari npm.htmx.orgadalah nama paket htmx di npm.@2.0.11mengunci versinya. Ini versi htmx 2 terbaru per Oktober 2026: 52.182 byte, sekitar 16,5 KB setelah dikompres gzip. Tanpa@versi, alamathttps://unpkg.com/htmx.orgmemberi versi terbaru apa pun, jadi isinya bisa berubah sendiri suatu hari.- Gak ada
npm install, gak ada build step. Taruh tag ini di<head>, dan htmx langsung membaca semua atributhx-di halaman.
Tombol pertama: Cek status
Ini sepotong papan order Bu Rini. Halamannya dirender server, dan cucian #12 masih tertulis "Dicuci".
<ul id="papan-order">
<li id="order-12">
#12 Pak Darto, 3 kg: <span id="status-12">Dicuci</span>
<button hx-get="/order/12/status" hx-target="#status-12">
Cek status
</button>
</li>
</ul>
hx-get="/order/12/status": saat tombol diklik, kirim request GET ke alamat itu. GET artinya "minta data", gak mengubah apa pun di server.hx-target="#status-12": balasannya ditaruh di elemen ber-idstatus-12, bukan di tombolnya sendiri. Isinya selector CSS, jadi#artinya id.<span id="status-12">adalah tempat tujuannya, dan dia harus ada di halaman.
hx-get dan hx-target itu atribut HTML biasa. Browser cuma menyimpannya, htmx yang membacanya. Itulah inti htmx: fitur AJAX (request di belakang layar tanpa reload) lewat atribut HTML.
Apa yang terjadi saat tombolnya diklik
| Langkah | Yang terjadi |
|---|---|
| 1. Klik | htmx melihat hx-get di tombol yang diklik |
| 2. Request | browser mengirim GET /order/12/status, plus header HX-Request: true (header adalah keterangan tambahan yang ikut di request) supaya server tahu ini dari htmx |
| 3. Balasan | server membalas HTML: Disetrika, cuma 9 byte |
| 4. Tukar | htmx menjadikan balasan itu isi #status-12. Bagian lain halaman tetap |
Sebelum diklik halamannya bertuliskan #12 Pak Darto, 3 kg: Dicuci, sesudahnya #12 Pak Darto, 3 kg: Disetrika. Baris JavaScript yang kamu tulis: 0. Itu jawaban tebakannya. JavaScript-nya tetap ada, tapi di dalam htmx, ditulis sekali oleh pembuatnya dan dipakai semua tombolmu.
Server membalas HTML, bukan JSON
Update yang sama bisa dikerjakan tiga cara. Ukurannya diukur di Lab halaman ini, untuk status yang sama:
| Cara | Yang dikirim server | Ukuran | JavaScript buatanmu |
|---|---|---|---|
Link biasa <a href="/papan"> |
seluruh halaman | 1.136 byte | 0 baris, tapi seluruh halaman digambar ulang |
| fetch + JSON | {"id":12,"status":"Disetrika"} |
30 byte | 5 baris |
| htmx | Disetrika |
9 byte | 0 baris |
Cara JSON butuh kode seperti ini di halaman:
<button id="cek-12">Cek status</button>
<script>
document.querySelector('#cek-12').addEventListener('click', async () => {
const res = await fetch('/api/order/12')
const order = await res.json()
document.querySelector('#status-12').textContent = order.status
})
</script>
- Dengan JSON, server mengirim data mentah, dan kode di browser yang mengubahnya jadi tampilan. Tiap layar baru butuh kode seperti ini lagi.
- Dengan htmx, server yang menyusun HTML-nya (di Laravel, dari Blade), dan browser tinggal menempelnya. Cara ini disebut hypermedia: server mengirim HTML yang siap tampil.
- Ukuran balasan bukan poin utamanya. Untuk satu kata, HTML lebih kecil dari JSON. Untuk daftar panjang, HTML biasanya sedikit lebih besar karena membawa tag. Yang hilang adalah kode render di browser.
Kapan htmx cocok
- Cocok: aplikasi yang halamannya dirender server (Laravel, Django, Rails, PHP): panel admin, form, daftar dengan filter, dashboard.
- Kurang cocok: aplikasi yang state-nya hidup di browser dan berubah tiap saat tanpa server, misalnya editor gambar, game, atau aplikasi yang harus jalan offline. Di sana SPA (single-page application, misalnya React atau Vue) lebih pas.
⚠️ Jebakan yang sering ketemu
- Server membalas JSON. htmx menaruh apa pun balasannya sebagai HTML, jadi
{"id":12,"status":"Disetrika"}tampil mentah-mentah di halaman. Balas dengan potongan HTML. - Lupa tag script. Browser mengabaikan atribut yang gak dikenalnya, tanpa error. Tombolnya cuma diam.
- Tempat tujuannya gak ada. Kalau tertulis
hx-target="#result"tapi gak ada elemenid="result", htmx bahkan gak mengirim request-nya. Di latihan, tombol dan#resultsama-sama dicek. - Versi gak dikunci.
https://unpkg.com/htmx.orgtanpa versi bisa berganti ke versi besar berikutnya (htmx 4 sudah terbit di npm dengan tagnext, dan suatu hari jadi yang terbaru). Tulis[email protected].
TL;DR: htmx memberi elemen HTML kemampuan AJAX lewat atribut hx-, dipasang dengan satu tag <script src="https://unpkg.com/[email protected]">. hx-get="/order/12/status" mengirim request GET saat tombol diklik, dan hx-target="#status-12" menunjuk elemen yang menerima balasannya. Server membalas potongan HTML (Disetrika, 9 byte), bukan JSON, dan htmx menaruhnya tanpa reload, dengan 0 baris JavaScript buatanmu. Link biasa mengirim ulang seluruh halaman (1.136 byte), fetch + JSON butuh kode render sendiri. htmx cocok untuk aplikasi yang dirender server, kurang cocok untuk aplikasi yang state-nya hidup di browser.