← Semua pembelajaran / Go Nol → Enterprise
Fase 7 · Framework Web & Frontend

templ & htmx — interaktif tanpa SPA

Untuk situs bertrafik baca tinggi, halaman yang dirender server dan bisa disimpan di CDN sulit dikalahkan. templ membuat template-nya bertipe; htmx menambahkan interaktivitas tanpa membangun SPA.

Sumber asli templ.guide Resmi Rangkuman ~7 menit baca

Intisari

  • templ mengompilasi template jadi fungsi Go — salah nama field jadi error kompilasi, bukan halaman rusak di produksi.
  • Keluarannya di-escape secara bawaan, sama seperti html/template.
  • htmx membuat elemen HTML biasa bisa mengirim permintaan dan mengganti sebagian halaman — server tetap mengembalikan HTML.
  • Kombinasi ini menghapus lapisan API JSON dan state ganda untuk seluruh kelas aplikasi.
  • Halaman publik tetap bisa di-cache penuh di CloudFront; hanya fragmen interaktif yang dinamis.

templ: template yang dikompilasi

// web/produk.templ
package web

templ DaftarProduk(produk []Produk, halaman int) {
	@Tata("Katalog") {
		<h1>Katalog</h1>

		<div id="daftar">
			for _, p := range produk {
				@KartuProduk(p)
			}
		</div>

		if len(produk) == 20 {
			<button
				hx-get={ fmt.Sprintf("/produk?halaman=%d", halaman+1) }
				hx-target="#daftar"
				hx-swap="beforeend"
				hx-indicator="#memuat">
				Muat lebih banyak
			</button>
		}
	}
}

templ KartuProduk(p Produk) {
	<article class="kartu">
		<h3>{ p.Nama }</h3>
		<p>{ rupiah(p.Harga) }</p>
	</article>
}
go get -tool github.com/a-h/templ/cmd/templ
go tool templ generate       # .templ → _templ.go
go build ./...
func (h *Handler) DaftarProduk(w http.ResponseWriter, r *http.Request) {
	produk, err := h.produk.Daftar(r.Context(), halaman)
	if err != nil {
		h.tulisError(w, r, err)
		return
	}

	// Permintaan htmx: kirim FRAGMEN saja, bukan halaman penuh.
	if r.Header.Get("HX-Request") == "true" {
		_ = web.PotonganProduk(produk).Render(r.Context(), w)
		return
	}

	w.Header().Set("Cache-Control", "public, s-maxage=60, stale-while-revalidate=600")
	_ = web.DaftarProduk(produk, halaman).Render(r.Context(), w)
}
html/templatetempl
Salah nama fieldError saat runtimeError kompilasi
Pelengkapan otomatis di editorTidakYa
Refactor namaManualIkut alat Go
EscapingSadar konteksSadar konteks
Langkah buildTidak adatempl generate
Bagian pustaka standarYaTidak

htmx: interaktivitas dari atribut HTML

<!-- Pencarian langsung: server mengembalikan HTML, bukan JSON -->
<input
  type="search" name="q"
  hx-get="/cari"
  hx-trigger="keyup changed delay:300ms"
  hx-target="#hasil"
  hx-indicator="#memuat">

<div id="hasil"></div>

<!-- Hapus dengan konfirmasi, tanpa satu baris JavaScript pun -->
<button
  hx-delete="/produk/42"
  hx-confirm="Yakin hapus produk ini?"
  hx-target="closest article"
  hx-swap="outerHTML swap:0.3s">
  Hapus
</button>
AtributFungsinya
hx-get, hx-post, hx-deleteKirim permintaan saat elemen dipicu
hx-targetElemen mana yang diganti responsnya
hx-swapBagaimana menggantinya: innerHTML, outerHTML, beforeend
hx-triggerPeristiwa pemicu, lengkap dengan debounce
hx-indicatorElemen yang ditampilkan selama menunggu

Yang hilang dengan pendekatan ini: lapisan API JSON, kode serialisasi, kode pengambilan data di klien, dan state yang harus dijaga sinkron di dua tempat. Yang tersisa: server mengembalikan HTML, browser menaruhnya. Untuk aplikasi CRUD, panel admin, dan situs konten, itu penghapusan kompleksitas yang besar — dan bonus terbesarnya adalah tidak ada lagi kemungkinan API dan UI berbeda pendapat tentang keadaan data.

Keamanan dengan htmx

  1. Fragmen HTML yang dikembalikan tetap harus di-escape — templ dan html/template melakukannya.
  2. Endpoint fragmen adalah endpoint sungguhan: butuh autentikasi, otorisasi, dan rate limit yang sama.
  3. Token CSRF ikut lewat hx-headers untuk permintaan yang mengubah data (Fase 6).
  4. Jangan mengembalikan fragmen berisi data pengguna dengan Cache-Control: public.
<body hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'>

Kapan pendekatan ini tepat

AplikasiPendekatan
Situs konten, katalog, blogtempl + CDN — hampir tanpa JavaScript
CRUD, panel admin, dasbor internaltempl + htmx
Formulir bertahap, filter, pencarian langsungtempl + htmx
Editor kolaboratif, kanvas, aplikasi offlineSPA sungguhan (React/Vue) + API JSON
Aplikasi mobileAPI JSON — HTML tidak membantu di sana

Untuk sasaran roadmap ini — situs skala enterprise dengan trafik baca tinggi — kombinasi paling sulit dikalahkan adalah: halaman publik dirender templ dan disajikan penuh dari CloudFront, dengan htmx hanya pada bagian yang memang interaktif. Sebagian besar permintaan tidak pernah menyentuh proses Go sama sekali (Fase 9), dan yang menyentuh cukup mengembalikan potongan HTML kecil.

Latihan: bangun halaman daftar produk dengan templ, lalu tambahkan tombol "muat lebih banyak" memakai htmx yang meminta fragmen dan menempelkannya. Pastikan permintaan htmx dikenali lewat header HX-Request dan hanya mengembalikan potongan. Lalu ganti nama satu field di struct Produk dan lihat go build menolak — itu yang tidak bisa dilakukan html/template.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.