BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

InstalasiInclude HTMXCore AttributesRequest Triggershx-triggerhx-targethx-swapSwap ModifiersData Submissionhx-valshx-headersURL dan Navigationhx-push-urlhx-boostIndicatorsLoading StatesEventsLifecycle EventsEvent Listening di JavaScriptCommon Event NamesServer Response HeadersHX-Trigger (Multiple Events)ExtensionsCommon ExtensionsPatternsInline EditInfinite ScrollDelete with AnimationSearch with DebounceConfirmation DialogJavaScript APIManual ControlServer-Side IntegrationExpress.js ResponseGlossary
HTMXHTMLWeb

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.

HTML8 min read1.431 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Instalasi

#Include HTMX

Cara termudah pakai HTMX di proyek kamu.

html
<!-- 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>

Verifikasi HTMX loaded:

html
<script>
document.addEventListener('DOMContentLoaded', function() {
  if (typeof htmx !== 'undefined') {
    console.log('HTMX version:', htmx.config.version);
  }
});
</script>

#Core Attributes

#Request Triggers

Atribut yang nentuin kapan HTMX ngirim request.

html
<!-- 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>
 
<!-- 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>

#hx-trigger

Nentuin event apa yang nge-trigger request.

html
<!-- 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>
 
<!-- 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>

#hx-target

Nentuin element mana yang akan diupdate dengan response.

html
<!-- this (default): element itu sendiri -->
<button hx-get="/data" hx-target="this">Load</button>
 
<!-- CSS selector -->
<button hx-get="/data" hx-target="#result">Load</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>

#hx-swap

Nentuin gimana response dimasukkin ke target.

html
<!-- innerHTML (default): ganti isi element -->
<div hx-get="/content" hx-swap="innerHTML">Loading...</div>
 
<!-- outerHTML: ganti element beserta isinya -->
<div hx-get="/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>

#Swap Modifiers

html
<!-- 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">
  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>

#Data Submission

#hx-vals

Kirim data tambahan bersama request.

html
<!-- JSON values -->
<button hx-get="/search"
        hx-vals='{"category": "tech", "sort": "desc"}'>
  Search
</button>
 
<!-- JavaScript computed values -->
<button hx-get="/data"
        hx-vals='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>

#hx-headers

html
<button hx-get="/api/data"
        hx-headers='{"X-Requested-With": "HTMX"}'>
  Load Data
</button>

#URL dan Navigation

#hx-push-url

Update URL bar browser tanpa reload.

html
<!-- 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="/page">Go to Page</a>
 
<!-- Hapus entry (replace) -->
<a hx-get="/home" hx-push-url="false">Home</a>

#hx-boost

Upgrade semua link dan form jadi AJAX request.

html
<!-- Boost semua link di dalam element -->
<div hx-boost="true">
  <a href="/page1">Page 1</a>
  <a href="/page2">Page 2</a>
</div>
 
<!-- Boost form -->
<form hx-boost="true" action="/search" method="get">
  <input name="q" type="text">
  <button>Search</button>
</form>

#Indicators

#Loading States

html
<!-- 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...</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>

CSS untuk indicator:

css
/* Default: hidden */
.htmx-indicator {
  opacity: 0;
  transition: opacity 200ms ease-in;
}
 
/* Show saat request */
.htmx-request .htmx-indicator,
.htmx-indicator.htmx-request {
  opacity: 1;
}
 
/* Disable element saat request */
.htmx-request.htmx-disabled-elt,
.htmx-disabled-elt.htmx-request {
  opacity: 0.5;
  pointer-events: none;
}

#Events

#Lifecycle Events

html
<!-- 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!')"
     hx-on::before-on-load="console.log('Processing response')">
  Load Data
</div>

#Event Listening di JavaScript

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);
});
 
// 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');

#Common Event Names

EventKapan dipicu
htmx:beforeRequestSebelum request dikirim
htmx:beforeSendSebelum XHR.send()
htmx:afterRequestSetelah request selesai (success atau error)
htmx:afterSwapSetelah content swap
htmx:beforeSwapSebelum swap
htmx:responseErrorResponse error (4xx, 5xx)
htmx:sendErrorNetwork error
htmx:timeoutRequest timeout
htmx:loadHTMX selesai load element
htmx:configRequestSebelum request config difinalize

#Server Response Headers

HTMX ngerti beberapa response header untuk control behavior.

python
# 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'
    # 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

#HX-Trigger (Multiple Events)

python
import json
 
# Multiple events dengan data
resp.headers['HX-Trigger'] = json.dumps({
    "showToast": {"message": "Saved!"},
    "updateCounter": {"count": 42}
})

#Extensions

#Common Extensions

html
<!-- Include extension script -->
<script src="https://unpkg.com/htmx-ext-sse@2.0.0/sse.js"></script>
 
<!-- Register extension -->
<body hx-ext="sse">
  <!-- 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>

#Patterns

#Inline Edit

html
<!-- 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"> -->

#Infinite Scroll

html
<div hx-get="/items?page=2"
     hx-trigger="revealed"
     hx-swap="afterend"
     hx-target="this">
  <div>Loading more...</div>
</div>

#Delete with Animation

html
<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>
</div>

#Search with Debounce

html
<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...">
 
<span id="search-indicator" class="htmx-indicator">
  Searching...
</span>
 
<div id="search-results"></div>

#Confirmation Dialog

html
<!-- 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>
 
<!-- 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>

#JavaScript API

#Manual Control

javascript
// Trigger request manually
htmx.trigger('#myElement', 'submit');
 
// AJAX helper methods
htmx.ajax('GET', '/api/data', {
  target: '#result',
  swap: 'innerHTML'
});
 
// 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);

#Server-Side Integration

#Express.js Response

javascript
app.get('/users/:id', async (req, res) => {
  const user = await getUser(req.params.id);
 
  // Cek apakah request dari HTMX
  if (req.headers['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 });
});

#Glossary

  • 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.

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: