belajarkoding Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.
© 2026 BelajarKoding. All rights reserved.
Bagian dari ekosistem Galih Pratama
HTMX Cheat Sheet Referensi cepat HTMX. Attribute hx-get, hx-post, hx-swap, hx-trigger, events, dan extensions. Perfect buat developer yang mau bikin web interaktif tanpa JavaScript berat.
Silakan
login atau
daftar untuk membaca cheat sheet ini.
Baca Cheat Sheet Lengkap Login atau daftar akun gratis untuk membaca cheat sheet ini.
HTMX Cheat Sheet - BelajarKoding | BelajarKoding
# Instalasi
# Include HTMX
Cara termudah pakai HTMX di proyek kamu.
<!-- Via CDN (paling cepat) -->
< script src = "https://unpkg.com/htmx.org@2.0.4" ></ script >
<!-- Via npm -->
< script src = "./node_modules/htmx.org/dist/htmx.min.js" ></
script
>
<!-- Self-hosted (production recommended) -->
< script src = "/assets/htmx.min.js" ></ script >
< script >
document. addEventListener ( 'DOMContentLoaded' , function () {
if ( typeof htmx !== 'undefined' ) {
console. log ( 'HTMX version:' , htmx.config.version);
}
});
</ script > Atribut yang nentuin kapan HTMX ngirim request.
<!-- hx-get: GET request saat element diklik -->
< button hx-get = "/api/users" >Load Users</ button >
<!-- hx-post: POST request -->
< button hx-post = "/api/users" hx-vals = '{"name": "John"}' >
Create User
</ button
Nentuin event apa yang nge-trigger request.
<!-- Default: click untuk button/link, submit untuk form, change untuk input -->
<!-- Trigger dengan event custom -->
< button hx-get = "/data" hx-trigger = "click" >Click Me</ button >
Nentuin element mana yang akan diupdate dengan response.
<!-- this (default): element itu sendiri -->
< button hx-get = "/data" hx-target = "this" >Load</ button >
<!-- CSS selector -->
< button hx-get = "/data" hx-target = "#result" >Load</
Nentuin gimana response dimasukkin ke target.
<!-- innerHTML (default): ganti isi element -->
< div hx-get = "/content" hx-swap = "innerHTML" >Loading...</ div >
<!-- outerHTML: ganti element beserta isinya -->
< div hx-get
<!-- Swap dengan transisi (morph, settle) -->
< div hx-get = "/content" hx-swap = "innerHTML transition:true" >
Content
</ div >
<!-- Swap dengan focus preservation -->
< div hx-get = "/form" hx-swap = "innerHTML focus:true"
Kirim data tambahan bersama request.
<!-- JSON values -->
< button hx-get = "/search"
hx-vals = '{"category": "tech", "sort": "desc"}' >
Search
</ button >
<!-- JavaScript computed values -->
< button hx-get = "/data"
hx-vals =
< button hx-get = "/api/data"
hx-headers = '{"X-Requested-With": "HTMX"}' >
Load Data
</ button > Update URL bar browser tanpa reload.
<!-- Push URL baru ke history -->
< a hx-get = "/users/1" hx-target = "#content" hx-push-url = "true" >
View User
</ a >
<!-- Push URL dari response header -->
< a hx-get = "/page" hx-push-url =
Upgrade semua link dan form jadi AJAX request.
<!-- Boost semua link di dalam element -->
< div hx-boost = "true" >
< a href = "/page1" >Page 1</ a >
< a href = "/page2" >Page 2</ a >
</ div >
<!-- hx-indicator: element yang di-show saat request loading -->
< button hx-get = "/slow-endpoint" hx-indicator = "#spinner" >
Load Data
</ button >
< span id = "spinner" class = "htmx-indicator" >Loading...</
/* Default: hidden */
.htmx-indicator {
opacity : 0 ;
transition : opacity 200 ms ease-in ;
}
/* Show saat request */
.htmx-request .htmx-indicator ,
.htmx-indicator.htmx-request {
opacity : 1
<!-- Event hooks -->
< div hx-get = "/data"
hx-on::before-request = "console.log('Request starting')"
hx-on::after-request = "console.log('Request done')"
hx-on::before-send = "console.log('About to send')"
hx-on::after-swap = "console.log('Content swapped')"
hx-on::response-error = "alert('Error!')"
# Event Listening di JavaScript// Listen untuk HTMX events
document.body. addEventListener ( 'htmx:afterRequest' , function ( evt ) {
console. log ( 'Request to:' , evt.detail.requestConfig.path);
console. log ( 'Status:' , evt.detail.xhr.status);
Event Kapan dipicu htmx:beforeRequest Sebelum request dikirim htmx:beforeSend Sebelum XHR.send() htmx:afterRequest Setelah request selesai (success atau error) htmx:afterSwap Setelah content swap htmx:beforeSwap Sebelum swap htmx:responseError Response error (4xx, 5xx) htmx:sendError Network error htmx:timeout Request timeout htmx:load HTMX selesai load element htmx:configRequest Sebelum request config difinalize
HTMX ngerti beberapa response header untuk control behavior.
# Python (Flask) example
from flask import Response
@app.route ( '/api/data' )
def data ():
resp = Response(html_content)
# Trigger client-side event
resp.headers[ 'HX-Trigger' ] = 'showToast'
# HX-Trigger (Multiple Events)import json
# Multiple events dengan data
resp.headers[ 'HX-Trigger' ] = json.dumps({
"showToast" : { "message" : "Saved!" },
"updateCounter" : { "count" : 42 }
}) <!-- Include extension script -->
< script src = "https://unpkg.com/htmx-ext-sse@2.0.0/sse.js" ></ script >
<!-- Register extension -->
< body hx-ext = "sse" >
<!-- Click to edit -->
< div id = "title" hx-get = "/edit/1" hx-target = "this" hx-swap = "outerHTML" >
< h2 >Click to edit title</ h2 >
</ div >
<!-- Server returns the edit form -->
<!-- <form id="title" hx-put="/edit/1" hx-target="this" hx-swap="outerHTML"> --> < div hx-get = "/items?page=2"
hx-trigger = "revealed"
hx-swap = "afterend"
hx-target = "this" >
< div >Loading more...</ div >
</ div > < div class = "item" id = "item-1"
hx-delete = "/items/1"
hx-target = "closest .item"
hx-swap = "outerHTML"
hx-on::after-request = "this.style.opacity = 0" >
Item content
< button >Delete</ button
< input type = "search"
name = "q"
hx-get = "/search"
hx-trigger = "input changed delay:300ms, search"
hx-target = "#search-results"
hx-indicator = "#search-indicator"
placeholder = "Search..." >
<!-- hx-confirm dengan custom message -->
< button hx-delete = "/items/1"
hx-confirm = "Yakin mau hapus item ini? Tindakan ini tidak bisa dibatalkan."
hx-target = "closest .item"
hx-swap = "outerHTML" >
Delete
</ button >
// Trigger request manually
htmx. trigger ( '#myElement' , 'submit' );
// AJAX helper methods
htmx. ajax ( 'GET' , '/api/data' , {
target: '#result' ,
swap: 'innerHTML'
});
app. get ( '/users/:id' , async ( req , res ) => {
const user = await getUser (req.params.id);
// Cek apakah request dari HTMX
if (req.headers[
Boost : Upgrade link/form standar jadi AJAX request. Halaman terasa kayak SPA tanpa nulis JS.
Swap : Cara HTMX masukkin response HTML ke element target. Default innerHTML.
Out of Band Swap : Update multiple element dari satu response pakai attribute hx-swap-oob.
Indicator : Element yang di-show saat request lagi loading. Biasanya spinner atau skeleton.
Revealed : Trigger saat element masuk viewport. Berguna buat lazy loading.
Extension : Plugin HTMX yang nambahin fungsionalitas baru (SSE, JSON encoding, dll).
Settle : Animasi transisi setelah HTMX swap content. CSS transition otomatis dimainkan.
>
<!-- hx-put: PUT request -->
< button hx-put = "/api/users/1" >Update</ button >
<!-- hx-delete: DELETE request -->
< button hx-delete = "/api/users/1" hx-confirm = "Yakin mau hapus?" >
Delete
</ button >
<!-- hx-patch: PATCH request -->
< button hx-patch = "/api/users/1" >Partial Update</ button >
<!-- Trigger saat input berubah -->
< input type = "text" hx-get = "/search" hx-trigger = "keyup"
hx-target = "#results" name = "q" >
<!-- Trigger dengan delay (debounce) -->
< input type = "text" hx-get = "/search" hx-trigger = "keyup changed delay:500ms"
hx-target = "#results" name = "q" >
<!-- Trigger saat element muncul di viewport -->
< div hx-get = "/lazy-load" hx-trigger = "revealed" >
Loading...
</ div >
<!-- Trigger saat page load -->
< div hx-get = "/notifications" hx-trigger = "load" >
Loading notifications...
</ div >
<!-- Multiple triggers -->
< button hx-get = "/data" hx-trigger = "click, keyup[ctrlKey && key=='r']" >
Refresh
</ button >
<!-- Trigger dari event lain -->
< div hx-get = "/updated" hx-trigger = "refreshData from:body" >
Waiting for refresh...
</ div >
<!-- Polling (request berkala) -->
< div hx-get = "/live-data" hx-trigger = "every 5s" >
Auto-refresh setiap 5 detik
</ div >
button
>
<!-- closest: cari ancestor terdekat -->
< button hx-get = "/data" hx-target = "closest .card" >Update Card</ button >
<!-- find: cari descendant pertama -->
< div hx-get = "/data" hx-target = "find .content" >Load</ div >
<!-- next / previous sibling -->
< button hx-get = "/data" hx-target = "next div" >Load</ button >
<!-- relative syntax (HTMX 2.0) -->
< button hx-get = "/data" hx-target = "closest div" >Load</ button >
=
"/new-card"
hx-swap
=
"outerHTML"
>Old Card</
div
>
<!-- beforebegin: sebelum element -->
< div hx-get = "/banner" hx-swap = "beforebegin" >Before me</ div >
<!-- afterbegin: di awal isi element (prepend) -->
< ul hx-get = "/new-item" hx-swap = "afterbegin" >
< li >Existing item</ li >
</ ul >
<!-- beforeend: di akhir isi element (append) -->
< ul hx-get = "/new-item" hx-swap = "beforeend" >
< li >Existing item</ li >
</ ul >
<!-- afterend: setelah element -->
< div hx-get = "/footer" hx-swap = "afterend" >After me</ div >
<!-- delete: hapus element -->
< button hx-delete = "/items/1" hx-target = "closest .item" hx-swap = "delete" >
Delete
</ button >
<!-- none: tidak swap apa-apa (berguna bareng hx-on) -->
< div hx-get = "/track" hx-swap = "none" >Track click</ div >
>
Form
</ div >
<!-- Swap dengan scroll -->
< div hx-get = "/more" hx-swap = "beforeend scroll:bottom" >
List
</ div >
<!-- Swap timing -->
< div hx-get = "/data" hx-swap = "innerHTML settle:500ms" >
Data
</ div >
'js:{timestamp: Date.now()}'
>
Load
</ button >
<!-- Include form values -->
< form hx-post = "/submit" hx-include = "[name='email']" >
< input name = "name" value = "John" >
< input name = "email" value = "john@test.com" >
< button >Submit</ button >
</ form >
"/page"
>Go to Page</
a
>
<!-- Hapus entry (replace) -->
< a hx-get = "/home" hx-push-url = "false" >Home</ a >
<!-- Boost form -->
< form hx-boost = "true" action = "/search" method = "get" >
< input name = "q" type = "text" >
< button >Search</ button >
</ form >
span
>
<!-- hx-disabled-elt: disable element saat request -->
< button hx-get = "/data" hx-disabled-elt = "this" >
Load Data
</ button >
<!-- Disable form saat submit -->
< form hx-post = "/submit" hx-disabled-elt = "button" >
< button >Submit</ button >
</ form >
;
}
/* Disable element saat request */
.htmx-request.htmx-disabled-elt ,
.htmx-disabled-elt.htmx-request {
opacity : 0.5 ;
pointer-events : none ;
}
hx-on::before-on-load = "console.log('Processing response')" >
Load Data
</ div >
});
// Cancel request
document.body. addEventListener ( 'htmx:beforeRequest' , function ( evt ) {
if (someCondition) {
evt. preventDefault ();
}
});
// Custom response handling
document.body. addEventListener ( 'htmx:beforeOnLoad' , function ( evt ) {
// Cek status code
if (evt.detail.xhr.status === 401 ) {
window.location.href = '/login' ;
evt. preventDefault ();
}
});
// Trigger custom event
htmx. trigger ( '#myElement' , 'refreshData' );
# Redirect client
resp.headers[ 'HX-Redirect' ] = '/dashboard'
# Refresh target
resp.headers[ 'HX-Refresh' ] = 'true'
# Retarget swap
resp.headers[ 'HX-Retarget' ] = '#other-element'
# Reswap
resp.headers[ 'HX-Reswap' ] = 'outerHTML'
return resp
<!-- Server-Sent Events -->
< div sse-connect = "/events" sse-swap = "message" hx-target = "this" hx-swap = "beforeend" >
<!-- Content updates from SSE -->
</ div >
</ body >
<!-- JSON Encoding -->
< script src = "https://unpkg.com/htmx-ext-json-enc@2.0.0/json-enc.js" ></ script >
< body hx-ext = "json-enc" >
<!-- hx-vals akan dikirim sebagai JSON body -->
< button hx-post = "/api" hx-vals = '{"key": "value"}' >Send JSON</ button >
</ body >
<!-- Debug extension -->
< script src = "https://unpkg.com/htmx-ext-debug@2.0.0/debug.js" ></ script >
< body hx-ext = "debug" >
<!-- Semua HTMX events akan di-log -->
</ body >
<!-- Remove me extension (remove element after swap) -->
< script src = "https://unpkg.com/htmx-ext-remove-me@2.0.0/remove-me.js" ></ script >
< div hx-ext = "remove-me" >
< div remove-me = "2s" >This will disappear in 2 seconds</ div >
</ div >
>
</ div >
< span id = "search-indicator" class = "htmx-indicator" >
Searching...
</ span >
< div id = "search-results" ></ div >
<!-- Custom confirm via event -->
< script >
document. addEventListener ( 'htmx:confirm' , function ( evt ) {
evt. preventDefault ();
if (evt.detail.question. includes ( 'irreversible' )) {
// Custom modal instead of native confirm()
showCustomModal (() => evt.detail. issueRequest ( true ));
} else {
evt.detail. issueRequest ( true );
}
});
</ script >
// Find HTMX element
htmx. find ( '#myElement' );
// Add/remove HTMX processing class
htmx. addClass ( '#myElement' , 'htmx-request' );
htmx. removeClass ( '#myElement' , 'htmx-request' );
// Get config value
httmx.config.includeIndicatorStyles;
// Parse HTML as HTMX
htmx. process (document.body);
'hx-request'
]) {
// Return HTML fragment
res. render ( 'partials/user-card' , { user });
} else {
// Return full page
res. render ( 'pages/user-detail' , { user });
}
});
// Partial render untuk HTMX
app. post ( '/tasks' , async ( req , res ) => {
const task = await createTask (req.body);
res. setHeader ( 'HX-Trigger' , 'taskCreated' );
res. setHeader ( 'HX-Retarget' , '#task-list' );
res. render ( 'partials/task-item' , { task });
});