Extension adalah tambahan yang memberi htmx kemampuan baru tanpa mengubah intinya: mengirim form sebagai JSON, menaruh balasan error di kotak lain, memuat halaman sebelum diklik. Di htmx 2, setiap extension adalah paket sendiri bernama htmx-ext-.... Ia dipasang dua langkah: dimuat dengan tag <script> sesudah htmx, lalu dinyalakan dengan hx-ext di elemen yang memakainya. Bayangin aksesori mesin cuci di Laundry Bu Rini. Pengering tambahan harus dibeli dan dipasang dulu (script), lalu dinyalakan di mesin yang mau memakainya (hx-ext). Menekan tombol pengering di mesin yang belum dipasangi gak menghasilkan apa-apa.
🧪 Tebakan cepat: Form Terima cucian punya
hx-ext="json-enc", tapi tag script extension-nya lupa dipasang. Kasir menekan Terima. Body-nya JSON, form-encoded, atau muncul error? Jawabannya ada di bagian "Memasang extension: muat, lalu nyalakan".
Memasang extension: muat, lalu nyalakan
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]"></script>
<body hx-ext="response-targets">
<form hx-post="/order" hx-ext="json-enc">...</form>
</body>
- htmx dulu, baru extension. Script extension memanggil
htmx.defineExtension(...), jadi kalau htmx belum dimuat, Console menulishtmx is not defined. hx-extdiwariskan ke semua isinya. Beberapa sekaligus dipisah koma:hx-ext="json-enc, response-targets".hx-ext="ignore:json-enc"mematikannya untuk bagian itu.- Nama di
hx-extyang gak dikenal htmx diabaikan tanpa error. Jadi tanpa script-nya, form tetap terkirim form-encoded seperti biasa. Itu jawaban tebakannya. - Alamat lama htmx 1,
htmx.org/dist/ext/json-enc.js, masih ada di paket htmx 2, tapi isinya extension versi htmx 1 dan Console memberi peringatan. Pakai pakethtmx-ext-.... - Dengan Vite (misalnya di Laravel):
npm install htmx-ext-json-enc, laluimport 'htmx-ext-json-enc'sesudah htmx.
json-enc: kirim form sebagai JSON
<form id="terima" hx-post="/order" hx-ext="json-enc" hx-target="#papan-order" hx-swap="beforeend">
<input name="nama" value="Bu Tini">
<input name="berat" value="4">
<label><input type="checkbox" name="layanan" value="setrika" checked> Setrika</label>
<label><input type="checkbox" name="layanan" value="lipat" checked> Lipat</label>
<button>Terima cucian</button>
</form>
| Tanpa json-enc | Dengan json-enc | |
|---|---|---|
Content-Type |
application/x-www-form-urlencoded |
application/json |
| Body | nama=Bu%20Tini&berat=4&layanan=setrika&layanan=lipat |
{"nama":"Bu Tini","berat":"4","layanan":["setrika","lipat"]} |
berattetap teks,"4", karena isi form selalu teks. Nilai darihx-valstetap tipenya:hx-vals='{"express": true}'jadi"express":true.- Nama yang muncul dua kali jadi array:
"layanan":["setrika","lipat"]. - Cuma body yang berubah. Request GET tetap menaruh nilainya di alamat.
- Laravel membaca JSON sama seperti form:
$request->input('nama'). json-enc berguna untuk API yang cuma menerima JSON.
response-targets: balasan error ke kotak lain
Dari pelajaran validasi: htmx 2 gak menukar balasan 4xx dan 5xx. response-targets memberi tujuan sendiri per status:
<body hx-ext="response-targets">
<div id="galat"></div>
<form id="terima" hx-post="/order" hx-target="#papan-order" hx-swap="beforeend"
hx-target-422="#galat">
...
</form>
<ul id="papan-order">...</ul>
</body>
| Balasan server | Mendarat di |
|---|---|
201, <li id="order-15">...</li> |
#papan-order, seperti biasa |
422, <p>Kapasitas hari ini tinggal 20 kg.</p> |
#galat |
| 500 tanpa aturan | gak ditukar |
- Pola nama atributnya:
hx-target-422persis,hx-target-4*(atau4xx) semua 4xx,hx-target-5*, danhx-target-erroruntuk semua 4xx dan 5xx. - Pasang
hx-ext="response-targets"di elemen yang memuat target-nya juga, misalnya<body>. Extension ini bekerja saat swap, di elemen target. Di form saja, dengan#papan-orderdi luar form, aturannya gak jalan. - Bedanya dengan
responseHandling(pelajaran validasi):responseHandlingmembuat 422 ditukar di target yang sama, response-targets mengirimnya ke kotak lain.
preload: mulai memuat sebelum diklik
<body hx-ext="preload">
<button hx-get="/laporan" hx-target="#laporan" preload="mouseover">Laporan hari ini</button>
- Bawaannya
preload(tanpa nilai) mulai memuat saat tombol mouse ditekan, 100 sampai 200 ms sebelum klik selesai.preload="mouseover"mulai sesudah kursor diam 100 ms di atasnya. Di layar sentuh, saat disentuh. - Request preload membawa header
HX-Preloaded: true. Balasannya disimpan di cache browser, jadi klik sungguhan mengambilnya dari cache, asal server mengizinkan, misalnyaCache-Control: private, max-age=60. - Contoh: server butuh 400 ms. Kasir mengarahkan kursor 600 ms sebelum mengklik. Tanpa preload, laporan muncul 400 ms sesudah klik. Dengan
preload="mouseover", balasannya sudah di cache, jadi muncul begitu diklik. - Cuma untuk GET (link dan
hx-get). POST dan DELETE gak pernah di-preload.
loading-states: indikator lewat data-loading
<style>[data-loading] { display: none; }</style>
<body hx-ext="loading-states">
<form id="terima" hx-post="/order" data-loading-states>
<button data-loading-disable>Terima cucian</button>
<span data-loading>Menyimpan...</span>
</form>
data-loadingterlihat selama request,data-loading-disablememasangdisabled,data-loading-classmenambah class.data-loading-delaymenunggu 200 ms dulu, supaya balasan cepat gak membuat indikator berkedip.data-loading-statesmembatasi wilayahnya. Tanpa itu, setiap request di halaman, termasuk Cek status, ikut menonaktifkan tombol Terima dan memunculkan Menyimpan...- Untuk kebutuhan sederhana,
hx-indicatordanhx-disabled-elt(pelajaran indikator loading) sudah cukup tanpa extension.
Satu lagi, cukup diketahui: head-support menggabungkan isi <head> (title, meta, CSS) dari balasan halaman yang di-boost. Pakai kalau halaman-halamanmu butuh CSS atau meta yang berbeda.
⚠️ Jebakan yang sering ketemu
hx-exttanpa script-nya, atau salah nama. htmx diam saja, tanpa error. Cek tab Network: body-nya masih form-encoded. Di latihan, halamannya butuh<script src="https://unpkg.com/[email protected]">, dan form-nya butuhhx-ext="json-enc",hx-post="/api/data", danhx-targetyang menunjuk id yang benar-benar ada (#result). Server contohnya cuma menjawabDiterima sebagai JSONkalau body-nya benar-benar JSON.- Script extension sebelum htmx.
htmx is not defined, dan extension-nya gak terdaftar. - response-targets cuma di form. Target di luar form gak ikut, jadi 422 tetap gak ditukar. Pasang
hx-extdi<body>. - loading-states tanpa
data-loading-states. Semua indikator di halaman bereaksi ke setiap request.
TL;DR: Di htmx 2, extension adalah paket htmx-ext-... sendiri: muat script-nya sesudah htmx, lalu nyalakan dengan hx-ext (diwariskan, dipisah koma, ignore: untuk mematikan). Tanpa script-nya, hx-ext diabaikan diam-diam. json-enc mengirim body sebagai application/json ({"nama":"Bu Tini","berat":"4"}). response-targets menaruh balasan per status di target lain (hx-target-422="#galat", hx-target-error), dengan hx-ext di elemen yang juga memuat target-nya. preload memuat GET lebih dulu saat tombol mouse ditekan atau kursor diam 100 ms, dan butuh cache yang diizinkan server. loading-states memakai data-loading, data-loading-disable, dan data-loading-delay, dibatasi dengan data-loading-states. head-support menggabungkan <head> dari halaman yang di-boost.