Framework

HTMX CodeIgniter 4 - Membuat Web Interaktif Tanpa JavaScript yang Ribet

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.

Apa Itu HTMX dan Kenapa Harus Peduli?

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:

  • Tidak perlu build step tinggal include satu file, langsung jalan
  • Server tetap render HTML controller CI4 kamu tetap return view, bukan JSON
  • Progressive enhancement web tetap jalan tanpa JS, HTMX menambah interaktivitas
  • Zero learning curve kalau kamu sudah paham HTML dan CI4 routing, kamu sudah siap

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.

Setup HTMX di CodeIgniter 4

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();?>">

Contoh 1: Live Search Tanpa JavaScript

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 dipanggil
  • hx-trigger="keyup changed delay:300ms" trigger setelah user berhenti mengetik 300ms (debounce otomatis!)
  • hx-target="#search-results" hasil dimasukkan ke div ini

Controller (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.

Contoh 2: CRUD Delete Tanpa Reload

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:

  1. User klik tombol
  2. Browser muncul confirmation dialog (native, bukan custom JS)
  3. Kalau OK, HTMX kirim DELETE request ke URL
  4. Server hapus data, return response
  5. HTMX hapus <tr> terdekat dari tabel dengan animasi 500ms

Controller:

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.

Contoh 3: Infinite Scroll untuk Blog Listing

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.

Tips CI4 + HTMX yang Jarang Dibahas

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; }

Perbandingan: HTMX vs jQuery AJAX vs React

  • Kompleksitas: HTMX paling sederhana (atribut HTML saja), jQuery di tengah (butuh JS), React paling kompleks (butuh build tool, state management, API layer)
  • Bundle size: HTMX 14KB, jQuery 87KB, React+ReactDOM 130KB+
  • Server-side rendering: HTMX native, jQuery manual, React butuh Next.js
  • Learning curve: HTMX belajar 30 menit, jQuery 1-2 hari, React 1-2 minggu
  • Kapan pakai HTMX: web app yang server-rendered (seperti CI4), form-heavy apps, admin dashboards
  • Kapan TIDAK pakai HTMX: SPA dengan offline capability, real-time collaboration (pakai WebSocket), app yang butuh complex client-side state

Kesimpulan

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.


You may also like


0 Comments


Leave a Reply

Comments with links or spam keywords will be rejected.
Scroll to Top