Pernah nggak sih kamu lagi bikin web app, lalu merasa bosan harus nulis JavaScript berlembar-lembar cuma buat update satu bagian halaman? Saya dulu juga gitu. Setiap kali butuh fitur interaktif seperti live search, form submission tanpa reload, atau infinite scroll, otomatis langsung buka file JS dan mulai nulis fetch(), addEventListener(), manipulasi DOM, dan seterusnya. Belum lagi kalau pakai framework frontend seperti React atau Vue, complexity-nya naik drastis.
Nah, di sinilah HTMX masuk. Library kecil (cuma 14KB gzipped) yang bikin kamu bisa bikin web interaktif cukup dengan atribut HTML biasa. Dan kabar baiknya, HTMX works beautifully dengan CodeIgniter 4. Artikel ini akan membahas cara integrasi lengkap beserta contoh-contoh praktis yang bisa langsung kamu pakai di project.
HTMX itu konsepnya sederhana: kamu bisa bikin request HTTP dari atribut HTML mana pun, bukan cuma dari <form> atau <a>. Mau bikin tombol yang menghapus data? Cukup tambah hx-delete. Mau dropdown yang load kontennya dari server? Pakai hx-get. Mau form yang submit tanpa reload? hx-post saja.
Yang bikin HTMX menarik untuk CI4 developer:
Bayangkan ini: kamu punya halaman dengan daftar produk. Tanpa HTMX, untuk filter by kategori kamu perlu nulis JS yang ambil data dari API, parse JSON, manipulasi DOM. Dengan HTMX? Cukup satu atribut di tombol filter, dan server CI4 kamu return HTML partial. Done.
Instalasi HTMX di CI4 gampang banget. Ada dua cara:
Cara 1: CDN (paling cepat untuk development)
<!-- Tambahkan sebelum closing </body> -->
<script src="https://unpkg.com/[email protected]"></script>
Cara 2: Download lokal (recommended untuk production)
# Download ke folder public
cd public/assets/js
curl -LO https://unpkg.com/[email protected]/dist/htmx.min.js
Lalu di layout CI4 kamu:
<script src="<?=base_url('assets/js/htmx.min.js');?>"></script>
Selesai. Sekarang setiap elemen HTML di halaman kamu bisa pakai atribut hx-*.
Satu hal penting: kalau kamu pakai CI4 dengan CSRF protection (yang seharusnya iya), kamu perlu konfigurasi HTMX supaya mengirim CSRF token di setiap request. Tambahkan ini di layout:
<meta name="htmx-config" content='{"includeIndicatorStyles": true}'>
<script>
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content;
});
</script>
Pastikan juga meta tag CSRF ada di <head>:
<meta name="csrf-token" content="<?=csrf_token();?>">
Ini contoh paling klasik dan paling sering dipakai. Kamu punya input search, user mengetik, dan hasil muncul real-time. Tanpa HTMX, kamu perlu nulis event listener, fetch request, parse response, inject ke DOM. Dengan HTMX?
View (search_form.php):
<div class="search-container">
<input type="text"
name="q"
placeholder="Cari artikel..."
hx-get="<?=site_url('blog/search');?>"
hx-trigger="keyup changed delay:300ms"
hx-target="#search-results"
hx-indicator="#loading">
<span id="loading" class="htmx-indicator">Loading...</span>
</div>
<div id="search-results"></div>
Tiga atribut itu saja sudah cukup:
hx-get URL yang akan dipanggilhx-trigger="keyup changed delay:300ms" trigger setelah user berhenti mengetik 300ms (debounce otomatis!)hx-target="#search-results" hasil dimasukkan ke div iniController (Blog.php):
public function search()
{
$query = $this->request->getGet('q');
if (empty($query)) {
return '';
}
$builder = $this->db->table('blog_posting');
$builder->like('Title', $query)
->orLike('Description', $query)
->where('Not_Active', 0)
->where('Account', 'default')
->orderBy('Created_Date', 'DESC')
->limit(10);
$results = $builder->get()->getResult();
$html = '';
foreach ($results as $row) {
$slug = str_replace(' ', '_', strtolower($row->Title));
$html .= '<div class="search-item">';
$html .= '<a href="' . site_url("blog/post/{$slug}") . '">' . esc($row->Title) . '</a>';
$html .= '<p>' . esc($row->Description) . '</p>';
$html .= '</div>';
}
if (empty($html)) {
$html = '<p class="text-muted">Tidak ditemukan hasil untuk "' . esc($query) . '"</p>';
}
return $html;
}
Perhatikan: controller return raw HTML string, bukan JSON. HTMX langsung inject ke target. Zero parsing, zero DOM manipulation.
Delete data biasanya butuh: buat link, bikin confirmation dialog, kirim DELETE request, hapus dari DOM. Dengan HTMX, cukup satu tombol:
<button hx-delete="<?=site_url('admin/posts/delete/' . $post->ID);?>"
hx-confirm="Yakin mau hapus artikel ini?"
hx-target="closest tr"
hx-swap="outerHTML swap:500ms">
<i class="fa fa-trash"></i> Hapus
</button>
Apa yang terjadi:
<tr> terdekat dari tabel dengan animasi 500msController:
public function delete($id = null)
{
if (!$this->request->is('delete')) {
return $this->response->setStatusCode(405);
}
$model = new M_blog();
$model->delete($id);
return $this->response->setStatusCode(200)->setBody('');
}
Bayangkan kalau kamu harus nulis ini di vanilla JS atau jQuery. Belum handle edge case-nya. Dengan HTMX, semua terjadi otomatis berkat atribut HTML.
Infinite scroll biasanya butuh library tersendiri atau kode JS yang cukup kompleks. Dengan HTMX, konsepnya elegan: halaman pertama memuat konten normal, dan di bagian bawah ada trigger yang otomatis load halaman berikutnya saat user scroll mendekati akhir.
View index (bagian listing):
<div id="post-list">
<?php foreach ($posts as $post): ?>
<div class="post-card">
<h4><?=$post->Title;?></h4>
<p><?=$post->Description;?></p>
</div>
<?php endforeach; ?>
</div>
<div hx-get="<?=site_url('blog/posts?page=2');?>"
hx-trigger="revealed"
hx-swap="afterend"
hx-indicator="#scroll-loading">
<div id="scroll-loading" class="htmx-indicator text-center">
<p>Memuat artikel lainnya...</p>
</div>
</div>
hx-trigger="revealed" itu kuncinya HTMX akan trigger request saat elemen ini masuk ke viewport (mirip Intersection Observer API, tapi tanpa nulis kode).
Controller untuk page berikutnya:
public function posts()
{
$page = $this->request->getGet('page') ?: 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$builder = $this->db->table('blog_posting');
$posts = $builder->where('Not_Active', 0)
->where('Account', 'default')
->orderBy('Created_Date', 'DESC')
->limit($perPage, $offset)
->get()
->getResult();
if (empty($posts)) {
return '';
}
$html = '';
foreach ($posts as $post) {
$html .= view('templates/blog_dark/partials/post_card', ['post' => $post]);
}
$nextPage = $page + 1;
$html .= '<div hx-get="' . site_url("blog/posts?page={$nextPage}") . '"'
. ' hx-trigger="revealed" hx-swap="afterend">'
. '<p class="text-center">Loading...</p></div>';
return $html;
}
Pola ini self-referencing: setiap response menyertakan trigger untuk page berikutnya. Kalau data habis, return kosong dan chain berhenti. Elegant.
1. Partial Response untuk HTMX Request
HTMX mengirim header HX-Request: true di setiap request. Kamu bisa pakai ini di controller untuk return partial HTML (tanpa layout) atau full page:
public function index()
{
$data['posts'] = $this->mBlog->List_Post('default');
if ($this->request->getHeaderLine('HX-Request') === 'true') {
return view('templates/blog_dark/partials/post_list', $data);
}
return view('templates/blog_dark/index', $data);
}
Ini menghemat bandwidth karena nggak perlu kirim layout berulang-ulang.
2. Push URL ke Browser History
Kalau user navigasi lewat HTMX, URL di address bar nggak berubah otomatis. Tambahkan hx-push-url="true":
<a href="<?=site_url('blog/post/my-article');?>"
hx-get="<?=site_url('blog/post/my-article');?>"
hx-target="#content"
hx-push-url="true">
Baca selengkapnya
</a>
3. Error Handling
document.body.addEventListener('htmx:responseError', function(evt) {
showToast('Terjadi error: ' + evt.detail.xhr.status);
});
4. Loading Indicator
HTMX punya class htmx-indicator yang otomatis show/hide:
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-block; }
HTMX bukan replacement untuk semua JavaScript framework. Tapi untuk 80% kasus web development sehari-hari terutama kalau kamu pakai server-side framework seperti CodeIgniter 4 HTMX adalah game changer. Kamu bisa bikin web yang interaktif tanpa complexity frontend build pipeline yang biasanya.
Coba mulai dari satu fitur kecil di project kamu. Misalnya, ganti form submission yang biasa kamu handle dengan jQuery AJAX ke HTMX. Rasakan bedanya. Kalau cocok, expand ke fitur lain. Saya yakin kamu bakal appreciate kesederhanaannya.
Sudah pernah coba HTMX? Atau masih setia dengan jQuery/vanilla JS? Share pengalaman kamu di kolom komentar di bawah.