1 poin oleh GN⁺ 4 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Sambil mempertahankan server-side rendering berbasis html/template di Go, HTMX menambahkan interaksi dinamis, dan satu renderer dapat secara selektif mengembalikan halaman penuh maupun fragmen HTML
  • Markup dipisah menjadi base.tmpl, template halaman, dan fragmen yang dapat digunakan ulang, lalu penyebaran dan struktur rendering disederhanakan dengan embed.FS dari Go 1.16 serta set template bersama
  • Bergantung pada HX-Request, hasil pencarian dikembalikan sebagai halaman penuh atau baris tabel, dan Vary: HX-Request serta historyRestoreAsHxRequest: false mencegah masalah cache dan pemulihan tombol kembali
  • Saat memindahkan permintaan HTMX ke halaman lain, gunakan HX-Redirect dan respons 2xx alih-alih 3xx biasa, dan kendalikan cakupan penggantian serta tampilan error dengan responseHandling untuk 204·422·4xx·5xx
  • Konfigurasi yang menonaktifkan cache local storage dan pewarisan atribut serta menetapkan batas waktu permintaan dijadikan titik awal, dan saat skala membesar, template layout menengah dapat ditambahkan untuk mendukung struktur layar terpisah seperti area admin

Mengapa menggabungkan server rendering Go dan HTMX

  • HTMX memungkinkan penambahan interaksi dinamis dengan sedikit JavaScript sambil mempertahankan konsistensi dan keamanan dari rendering HTML sisi server milik Go html/template
  • Implementasi ini membahas tiga sumbu utama
    • Struktur template yang mendukung respons halaman penuh dan HTML parsial sekaligus
    • Penanganan redirect dan error di lingkungan HTMX
    • Konfigurasi yang mengubah nilai bawaan HTMX dan alasannya
  • Dimulai dari fungsi dasar yang mengganti tombol dengan gambar, lalu diperluas menjadi layar pencarian yang memfilter daftar saat nama pengguna atau email diketik
  • Pola template yang sama dapat diterapkan bukan hanya ke HTMX tetapi juga ke alat HTML-over-the-wire seperti Unpoly dan Hotwire
  • Jika belum mengetahui penggunaan dasar HTMX, sebaiknya lihat dokumentasi resminya terlebih dahulu

Struktur direktori proyek dan konfigurasi file statis

  • Proyek dibagi ke area berikut sesuai perannya
    • assets/html/base.tmpl: kerangka HTML yang dibagikan semua halaman
    • assets/html/pages/: konten tiap halaman
    • assets/html/partials/: fragmen HTML yang digunakan ulang di banyak lokasi
    • assets/static/css, assets/static/img, assets/static/js: aset statis
    • cmd/web/: kode untuk memulai server, handler, dan renderer HTML
  • Kerangka dasar dibuat dengan perintah berikut
go mod init example.com/htmx
mkdir -p assets/static/css assets/static/img assets/static/js assets/html/partials assets/html/pages cmd/web
touch assets/efs.go assets/html/base.tmpl assets/html/partials/images.tmpl assets/html/pages/home.tmpl cmd/web/main.go cmd/web/handlers.go cmd/web/html.go
  • HTMX bisa dipasang lewat CDN atau NPM, tetapi pendekatan yang umum dipakai adalah mengunduh salinannya dan menyajikannya langsung sebagai file statis aplikasi
    • Konfigurasinya sederhana dan menghindari kekurangan yang muncul saat menggunakan CDN
    • Digunakan htmx.org@2.0.10 dan framework CSS tanpa class bamboo.css@1.4.0
wget -P assets/static/js https://cdn.jsdelivr.net/npm/htmx.org@2.0.10/dist/htmx.min.js
wget -P assets/static/css https://cdn.jsdelivr.net/npm/bamboo.css@1.4.0/dist/bamboo.min.css

Memisahkan template dasar, halaman, dan parsial

  • base.tmpl mendefinisikan kerangka seluruh dokumen seperti <head>, judul umum, dan <main>, lalu menyisipkan page:title dan page:content
  • Bamboo CSS dan skrip HTMX dimuat dari template dasar, dan skrip HTMX diberi atribut defer
    • Browser mengambil skrip secara paralel saat mem-parsing HTML
    • Skrip dijalankan setelah parsing HTML dan pembentukan DOM selesai
  • Semua template diberi nama eksplisit dengan {{define}}...{{end}} tanpa bergantung pada nama file
    • Kode Go dapat merujuk template secara konsisten hanya dengan nama yang didefinisikan
    • : pada page:title hanyalah pemisah bebas, dan nama seperti page_title, page-title, pageTitle, title juga bisa dipakai
  • Tombol di halaman utama menetapkan dua perilaku
    • hx-get="/gopher": mengirim permintaan GET /gopher saat diklik
    • hx-swap="outerHTML": mengganti tombol itu sendiri dengan HTML respons
  • partial:image:gopher adalah fragmen gambar yang dapat digunakan ulang, dan menerima lebar gambar saat dipanggil melalui width="{{.}}"
  • Fragmen HTML yang hanya dipakai di halaman tertentu dapat diletakkan di file halaman tersebut, bukan di direktori partials bersama
    • Fragmen yang dipakai lintas halaman ditempatkan di direktori bersama, sedangkan fragmen khusus halaman diletakkan di samping konten terkait

Menyertakan HTML dan aset statis ke dalam biner Go

  • Sejak embedding file ditambahkan di Go 1.16, HTML dan aset statis dapat disertakan ke dalam biner Go alih-alih dibaca dari disk saat runtime
  • Dengan direktif //go:embed "html" "static", kedua direktori dimasukkan ke embed.FS, lalu dipisah menjadi dua sub-filesystem menggunakan fs.Sub()
    • HTMLFiles: filesystem template dengan html sebagai root
    • StaticFiles: filesystem file statis dengan static sebagai root
  • Ada dua keuntungan dari pemisahan ini
    • Kode HTML dan kode file statis tidak mengakses file satu sama lain secara tidak perlu
    • Tidak perlu menambahkan prefiks html/ atau static/ saat membuka file
  • Jika tidak ingin memakai panic() di sub(), Anda juga bisa mengembalikan error dan menginisialisasi dua variabel itu di main()
  • Dalam implementasi saat ini, agar fs.Sub() gagal, nilai direktori harus berupa path yang tidak valid, tetapi string tetap "html" dan "static" lolos pemeriksaan ini, sehingga risiko panic saat runtime sangat rendah

Set template bersama dan htmlRenderer

  • htmlRenderer mengelola alur rendering seperti berikut
    • Saat mulai, ia mem-parsing set template bersama sekali saja
    • Set bersama itu dikloning setiap kali rendering dilakukan
    • Template halaman yang dibutuhkan permintaan lalu diparsing tambahan
    • Template dengan nama yang ditentukan dijalankan dan dikirim sebagai respons HTTP
  • newHTMLRenderer() mendaftarkan template.FuncMap lalu mem-parsing template umum
    • now dihubungkan ke time.Now
    • Fungsi template kustom lain juga dapat ditambahkan di lokasi yang sama
  • render() membuat set template per permintaan dengan sharedTemplates.Clone(), lalu memperluasnya dengan ParseFS() jika ada additionalTemplateFiles
  • Template dijalankan terlebih dahulu ke bytes.Buffer, lalu status code dicatat dan isi respons dikirim
  • Saat inisialisasi server, "base.tmpl" dan "partials/*.tmpl" diparsing sebagai set bersama
    • Template dasar dan semua fragmen umum selalu tersedia di renderer
    • Hanya template per halaman yang ditambahkan saat handler dipanggil
  • File statis disajikan melalui http.FileServerFS(assets.StaticFiles) pada path /static/, dan server berjalan di :5051

Merender halaman penuh dan fragmen HTML

  • Handler halaman utama mengembalikan dokumen penuh dengan pemanggilan berikut
app.html.render(w, 200, nil, "base", "pages/home.tmpl")
  • Pemanggilan ini menambahkan pages/home.tmpl ke set bersama lalu menjalankan template base dengan 200 OK
  • Handler /gopher menjalankan hanya partial:image:gopher yang sudah termasuk di set bersama, tanpa mem-parsing file tambahan
width := 100
app.html.render(w, http.StatusOK, width, "partial:image:gopher")
  • HTMX mengganti tombol dengan gambar yang diterima dari /gopher sesuai pengaturan hx-swap="outerHTML"
  • Struktur ini memberikan keuntungan berikut
    • Template dan aset statis disertakan dalam biner sehingga deployment menjadi lebih mudah
    • render() yang sama dapat mengembalikan halaman HTML penuh maupun fragmen HTML tertentu
    • Duplikasi markup berkurang dengan template dasar dan template parsial
    • Template parsial dapat digunakan kembali dari template dasar, konten halaman, maupun template parsial lainnya

Pencarian pengguna yang diperbarui saat mengetik

  • Fitur pencarian dibagi menjadi dua rute
    • GET /users: halaman HTML penuh yang memuat semua informasi pengguna
    • GET /users/search: fragmen HTML yang hanya mengembalikan baris tabel pengguna yang nama atau emailnya cocok dengan kata kunci pencarian
  • Kolom input pencarian menggabungkan atribut HTMX berikut
    • Mengirim permintaan pencarian dengan hx-get="/users/search"
    • Dengan hx-trigger="input changed delay:500ms, keyup[key=='Enter']", permintaan dikirim setelah 500ms sejak input berubah atau saat Enter ditekan
    • Kata kunci pencarian dikirim sebagai query string dalam bentuk GET /users/search?query=foo
    • Dengan hx-target="#search-results", respons dimasukkan ke dalam <tbody id="search-results">
    • Dengan hx-push-url="true", bilah alamat diperbarui pada setiap permintaan dan entri ditambahkan ke riwayat browser
  • users:rows adalah template terpisah yang hanya me-render baris pengguna
    • Setiap baris menampilkan nama dan email
    • Jika IsGopher bernilai true, partial:image:gopher di-render dengan lebar 24
  • listUsers dan searchUsers sama-sama menambahkan pages/users.tmpl ke himpunan bersama, tetapi template yang dieksekusi berbeda
    • listUsers: mengeksekusi base untuk mengembalikan halaman penuh
    • searchUsers: mengeksekusi users:rows untuk hanya mengembalikan baris yang cocok
  • Jika kata kunci pencarian kosong, daftar semua pengguna digunakan; jika ada nilainya, nama dan email diperiksa dengan strings.Contains()
  • Dalam aplikasi nyata, perbaikan berikut bisa dipertimbangkan
    • Menggabungkan handler daftar dan pencarian menjadi satu
    • Memusatkan penanganan error dan helper logging
    • Menambahkan header Content Security Policy dan pemulihan panic melalui middleware
    • Menetapkan timeout server secara tepat

Membedakan halaman penuh dan respons parsial dengan HX-Request

  • Jika pengguna membuka /users/search?query=leo langsung di browser atau mengunjungi lewat tautan yang dibagikan, yang terlihat mungkin hanya HTML parsial untuk baris tabel
  • Permintaan HTMX selalu menyertakan header HX-Request: true, sehingga ini dijadikan dasar untuk menentukan bentuk respons
func isHTMXRequest(r *http.Request) bool {
    return r.Header.Get("HX-Request") == "true"
}
  • Template eksekusi default untuk handler pencarian diatur ke base, lalu hanya diubah menjadi users:rows saat permintaan berasal dari HTMX
    • Jika dikunjungi langsung, pengguna menerima halaman penuh yang berisi hasil pencarian
    • Untuk permintaan HTMX, hanya fragmen baris yang akan dimasukkan ke dalam <tbody> yang dikembalikan

Header Vary dan pemulihan saat kembali

  • Karena URL yang sama dapat menghasilkan respons berbeda tergantung nilai HX-Request, cache juga perlu diberi tahu tentang perbedaan ini
  • Tambahkan Vary: HX-Request ke semua respons render
    • Secara ketat, lebih hemat jika hanya diatur pada handler yang memang membutuhkannya
    • Namun, mengaturnya sekaligus di renderer membantu menghindari bug akibat ada yang terlewat
  • Saat hx-push-url atau hx-boost menambahkan riwayat browser, HTMX menyimpan cache HTML halaman lengkap di penyimpanan lokal browser
    • Cache default maksimal 10 halaman
    • Jika pengguna mundur ke titik yang tidak ada di cache, HTMX akan meminta ulang URL tersebut ke server
  • Permintaan pemulihan default menyertakan HX-Request: true, sehingga server bisa mengembalikan HTML parsial alih-alih halaman penuh
  • Jika jenis respons dipilih berdasarkan HX-Request, atur historyRestoreAsHxRequest: false
    • Menghilangkan HX-Request: true dari permintaan pemulihan saat cache miss
    • Ini membuat server mengembalikan halaman HTML penuh untuk permintaan tersebut
  • Konfigurasi HTMX ditempatkan di tag meta htmx-config dalam base.tmpl

Mengalihkan permintaan HTMX ke halaman lain

  • HTMX dapat langsung mengganti fragmen HTML seperti pesan sukses, sehingga kebutuhan akan alur Post/Redirect/Get yang umum menjadi berkurang
  • Jika perlu memindahkan pengguna ke halaman yang sepenuhnya berbeda, seperti ke profil setelah login berhasil, ini tidak bisa ditangani hanya dengan respons 3xx biasa
    • Browser mengikuti respons 3xx lebih dulu daripada HTMX
    • HTMX hanya melihat respons akhir setelah redirect lalu mengganti target yang ada dengan konten tersebut
  • Untuk permintaan HTMX, gunakan respons 2xx bersama header HX-Redirect
    • HX-Redirect: /foo/bar memindahkan browser ke URL tersebut dan memuat ulang seluruh halaman
    • Permintaan ke tujuan pengalihan tidak menyertakan HX-Request: true
  • Jika ingin tetap mendukung lingkungan saat JavaScript dimatikan atau HTMX tidak dimuat, untuk permintaan non-HTMX tetap pertahankan respons 3xx biasa
func redirect(w http.ResponseWriter, r *http.Request, url string, code int) {
    if isHTMXRequest(r) {
        w.Header().Set("HX-Redirect", url)
        w.WriteHeader(http.StatusNoContent)
        return
    }

    http.Redirect(w, r, url, code)
}
  • Saat berpindah ke /profile setelah login, pemanggilannya bisa seperti berikut
redirect(w, r, "/profile", http.StatusSeeOther)

Perbedaan HX-Redirect dan HX-Location

  • HX-Location melakukan perilaku mirip redirect tanpa memuat ulang seluruh halaman
    • Mengambil HTML dari URL tujuan
    • Mengganti respons ke dalam <body>
    • Menambahkan entri baru ke riwayat browser
  • Saat meminta URL tujuan, HX-Request: true akan disertakan
  • Masalah bisa muncul jika rute tujuan mengembalikan HTML parsial berdasarkan HX-Request
    • Handler tidak bisa membedakan permintaan yang datang melalui HX-Location dan permintaan HTMX biasa
    • Yang pertama membutuhkan halaman penuh, sedangkan yang kedua membutuhkan respons parsial
  • Berbeda dengan pemulihan riwayat, tidak ada pengaturan untuk menonaktifkan HX-Request: true pada perpindahan HX-Location
  • Dalam banyak kasus, lebih mudah dan aman menggunakan HX-Redirect meski harus memuat ulang seluruh halaman
  • Jika rute tujuan memang selalu mengembalikan HTML penuh, HX-Location juga bisa digunakan

Menangani respons 204·422·4xx·5xx

  • Secara default, HTMX tidak mengganti DOM untuk respons 4xx atau 5xx
    • Layar saat ini tetap dipertahankan
    • Error dicatat di konsol developer tools
  • Bahkan jika server mengembalikan 500 Internal Server Error karena nama template tidak ada, dalam konfigurasi default pengguna tidak bisa melihat error itu di layar
  • Untuk menampilkan pesan error kepada pengguna, atur agar respons 4xx dan 5xx mengganti seluruh <body>
  • Pengecualiannya adalah 422 Unprocessable Content
    • Digunakan saat mengembalikan form yang berisi error validasi
    • Responsnya harus tetap diganti secara normal ke target HTMX yang ada
  • responseHandling dikonfigurasi menurut status kode sebagai berikut
    • 204 No Content: tidak mengubah DOM
    • 422 Unprocessable Content: diganti normal ke target yang ada
    • 4xx dan 5xx lainnya: respons diganti ke <body>
    • Respons selain itu: diganti normal ke target yang ada
"responseHandling":[
    {"code":"204", "swap": false},
    {"code":"422", "swap": true},
    {"code":"[45]..", "swap": true, "target": "body"},
    {"code":"...", "swap": true}
]
  • Jika pada interaksi tertentu error perlu dimasukkan ke target selain <body>, Anda bisa menimpa pengaturan default dengan ekstensi response target

Memeriksa URL saat ini di bilah alamat browser

  • HTMX tidak mengubah URL browser selama proses permintaan AJAX dan penggantian respons kecuali menggunakan hx-boost, hx-push-url, atau hx-replace-url
  • Karena itu, r.URL pada handler Go dan URL yang dilihat pengguna di bilah alamat bisa berbeda
  • HTMX mengirim URL yang saat ini ditampilkan browser melalui header HX-Current-URL pada setiap permintaan
  • Fungsi pembantu mem-parsing header ini menjadi url.URL, dan jika header tidak ada maka mengembalikan r.URL
func browserURL(r *http.Request) (*url.URL, error) {
    cu := r.Header.Get("HX-Current-URL")
    if cu != "" {
        return url.Parse(cu)
    }

    return r.URL, nil
}

Konfigurasi untuk mengubah nilai default HTMX

  • Dengan historyCacheSize: 0, cache halaman local storage milik HTMX dimatikan sepenuhnya
    • Cache local storage bisa menjadi sumber bug dan masalah keamanan
    • Saat menekan tombol kembali, HTML akan diambil ulang dari server alih-alih dari cache
    • Pada versi HTMX mendatang, caching local storage juga direncanakan akan nonaktif secara default karena alasan yang sama
  • Dengan disableInheritance: true, pewarisan atribut HTMX dinonaktifkan
    • Mendeklarasikan atribut secara eksplisit selalu lebih jelas
    • Mengurangi risiko bug atau perilaku yang tidak diinginkan
    • Pada versi HTMX mendatang, pewarisan atribut juga direncanakan akan nonaktif secara default
  • Dengan includeIndicatorStyles: false, HTMX tidak akan menyuntikkan style indikator
    • Style indikator didefinisikan sendiri bersama aturan CSS lain agar konsisten
  • Permintaan HTMX pada dasarnya tidak memiliki batas waktu, sehingga akan menunggu sampai server merespons
    • Bergantung pada cara aplikasi Go menangani timeout dan deadline, Anda bisa menentukan timeout dalam milidetik
    • Sebagai titik awal, gunakan timeout: 5000
  • Konfigurasi default juga mencakup historyRestoreAsHxRequest: false dan responseHandling berdasarkan kode status
<meta
    name="htmx-config"
    content='{
        "includeIndicatorStyles": false,
        "historyCacheSize": 0,
        "historyRestoreAsHxRequest": false,
        "responseHandling":[
            {"code":"204", "swap": false},
            {"code":"422", "swap": true},
            {"code":"[45]..", "swap": true, "target": "body"},
            {"code":"...", "swap": true}
        ],
        "timeout": 5000
    }'
>

Layout per halaman untuk aplikasi besar

  • Jika hanya template dasar dan template per halaman tidak cukup, Anda bisa menambahkan template layout di antara dua lapisan tersebut
  • Ini cocok untuk area seperti halaman admin yang membutuhkan judul, menu navigasi, dan struktur isi yang berbeda dari halaman biasa
  • base memanggil layout alih-alih langsung memanggil page:content
<body>
    {{template "layout" .}}
</body>
  • layouts/admin.tmpl mendefinisikan struktur umum area admin dan memanggil page:content di dalamnya
    • Judul area admin
    • Menu navigasi menuju pengguna dan pesanan
    • <main> tempat konten per halaman ditampilkan
  • Handler meneruskan layout yang akan digunakan bersama template halaman ke render()
app.html.render(
    w,
    200,
    nil,
    "base",
    "layouts/admin.tmpl",
    "pages/admin-orders.tmpl",
)
  • Dengan tetap mempertahankan template dasar bersama, Anda bisa menggabungkan layout per rute dan konten halaman, sehingga layar yang memerlukan struktur terpisah seperti area admin juga dapat diperluas dengan pola rendering yang sama

1 komentar

 
GN⁺ 4 jam lalu
Opini Hacker News
  • Menyukai Go + HTMX dan menggunakannya bersama a-h/templ untuk meningkatkan type safety pada template, komponen, dan HTML parsial Ia menyusun seluruh toolchain dengan Go·Unix·SQLite, menamainya stack GUS, dan telah mempublikasikannya: https://housecat.com/blog/the-gus-stack-go-unix-sqlite Stack ini sangat terinspirasi oleh stack GUTS milik exe.dev, tetapi menggunakan HTMX alih-alih TypeScript: https://exe.dev/docs/guts Untuk error stack trace ia memakai cockroachdb/errors, untuk HTML yang type-safe memakai templ, untuk pembuatan spesifikasi OpenAPI memakai fuego, untuk generasi kode SQL memakai sqlc, untuk SQLite murni Go memakai modernc.org/sqlite, untuk migrasi memakai goose, untuk workflow persistensi memakai dbos, dan untuk pengujian serta otomasi Chrome/CDP memakai rod Kombinasi ini produktif baik saat menulis kode sendiri maupun dengan bantuan agen, termasuk dalam proses build dan deploy binary tunggal

    • Saat membahas HTMX, sayang sekali Hyperscript tidak mendapat perhatian yang semestinya Dengan itu, DOM bisa dimanipulasi tanpa bolak-balik ke server atau fungsi JavaScript terpisah, sehingga sangat cocok dengan Go + HTMX dan terasa seperti perpanjangan dari HTML yang memang sedang dikerjakan Mungkin ada yang tidak menyukai pemrograman deklaratif, tetapi di Open Payment Host, manipulasi DOM yang kompleks untuk menangani berbagai payment gateway pun bisa diimplementasikan hanya dengan Hyperscript https://hyperscript.org/, https://openpaymenthost.com/
    • HTMX, atau gabungan hypertext·Unix·Go·SQLite, juga bisa diganti namanya menjadi stack HUGS
    • Ada juga yang menyebut Go·Templ·HTMX sebagai stack GoTH, dan jika ditambah sqlc menjadi GoTHs atau GHosT Jika go generate ./... diterima sebagai tahap build, banyak manfaat yang didapat, dan dengan goverter konversi antara model sqlc serta objek template·nilai kembalian juga bisa digenerasikan Sekitar 50% boilerplate dihasilkan otomatis sehingga type safety menjadi sangat baik
    • Belakangan ini ia sering memakai jet untuk SQL yang type-safe Memang butuh database yang sedang berjalan untuk code generation, tetapi itu juga dianggap kelebihan karena memaksa migrasi diterapkan terlebih dahulu sebelum menulis kode https://github.com/go-jet/jet
    • Untuk web app responsif kecil seperti dashboard API real-time yang mungkin bahkan tidak memerlukan database sendiri, belakangan ini ia sering memakai template PAHG Ini adalah kombinasi Pico.css·Alpine.js·HTMX·Go, dan tentu saja tetap bisa dihubungkan ke SQLite https://github.com/hiAndrewQuinn/pahg-template
  • HTMX sangat bagus untuk banyak kegunaan, tetapi sangat sulit diadopsi jika rekan satu tim tidak setuju Selalu ada penolakan bahwa ini bukan teknologi yang serius, dan berbagai bug mula-mula disalahkan pada HTMX; meski belakangan terbukti hanya salah paham, kepercayaan telanjur rusak Bahkan di tim terbaik tempat ia pernah bekerja pun tetap begitu, dan pelajarannya adalah pada akhirnya harus memilih pertarungan yang layak diperjuangkan Ia juga menganggap html/template milik Go punya antarmuka yang terasa janggal. Seperti anjuran dalam 《A Philosophy of Software Design》, kompleksitas seharusnya didorong ke dalam, tetapi struktur yang mengharuskan kita memikirkan duplikasi template setiap kali merender HTML terasa kompleks tanpa perlu

    • Aku juga menyukai HTMX, tetapi mengalami kenyataan yang sama saat mencoba mengadopsinya di tim Aku pernah mengusulkannya untuk web app sederhana, tetapi developer yang selama bertahun-tahun terbiasa mengembalikan JSON lalu merender dengan JS/jQuery kesulitan menyelesaikan masalah lama dengan pendekatan baru di setiap tahap, developer lain bahkan tidak mencoba, dan di tim orang-orang terus saja membicarakan Blazor Pendekatan baru apa pun mungkin akan terasa sulit, tetapi HTMX praktis tidak pernah benar-benar mendapat kesempatan Padahal untuk tim kecil, jika 95% JavaScript dihindari dan kode serta HTML dihasilkan di server, codebase akan lebih rapi dan pengujian juga lebih mudah. Sayang sekali. Di proyek pribadi aku tetap memakai HTMX, menemukan alur kerja yang baik, dan merasa pengembangan web menjadi jauh lebih sederhana
    • Dalam peran sebagai konsultan, ia aktif membimbing tim dan menjelaskan bahwa mempelajari HTMX justru memperdalam pemahaman tentang browser, yang merupakan lingkungan kerja sesungguhnya, sementara React dan sejenisnya malah mengisolasi developer dari browser, tetapi aku pun mengalami hal yang sama: https://www.reddit.com/r/htmx/s/DuXyGgsCWK html/template bagus karena memiliki keamanan dasar dan merupakan bagian standar bawaan, tetapi karena sifatnya yang dinamis terlalu banyak bagian yang tidak memiliki jawaban pasti, dan setelah menulis beberapa aplikasi komersial skala besar dengan Templ pun terasa kurang nyaman dari sisi developer experience Karena itu ia membuat gsx: https://github.com/gsxhq/gsx
    • Tidak perlu menduplikasi template setiap kali merender template; cukup kompilasi sekali lalu jalankan Masalah di tulisan aslinya muncul karena penulis mencoba menetapkan judul halaman dari template utama, sehingga itu sebenarnya masalah buatan sendiri. Cukup kirim judul lewat konteks, pecah base.tmpl menjadi BASE_BEGIN dan BASE_END, lalu gunakan itu di template akhir Judul per halaman memang harus diteruskan saat runtime, tetapi pendekatan di tulisan aslinya pun pada akhirnya akan rusak ketika dukungan multibahasa mulai diterapkan
    • Di perusahaan kami justru kebalikannya: meski penuh masalah, mustahil meyakinkan orang bahwa React SPA tidak bisa berkembang hanya karena dianggap teknologi yang sudah mapan Setiap masalah dilihat secara terpisah, tetapi orang-orang tidak bisa menghubungkannya ke gambaran besar bahwa semuanya berasal dari struktur yang sama
  • Dalam proyek terbaru, ia sangat menikmati penggunaan HTMX Dari sudut pandang orang yang mengenal web sebelum era AngularJS dan React, hal terbaiknya adalah bisa membuat halaman nyata dan meminimalkan jumlah JavaScript Semua ini sebenarnya bisa dilakukan dengan JavaScript murni, tetapi HTMX menggantikan boilerplate penanganan event yang berulang Jika tidak menyukai filosofi frontend modern, ini layak dicoba; contoh-contoh pertama di situs resminya memang sangat dasar, tetapi setelah dipelajari sedikit lebih jauh ternyata jauh lebih kuat

    • Dengan hati-hati, ia juga ingin merekomendasikan SvelteKit Dari luar memang tampak seperti framework frontend JavaScript lainnya, tetapi setelah benar-benar dipakai, ternyata sangat mendukung desain yang berpusat pada hypertext seperti yang dikejar keluarga HTMX
  • Saya pernah membuat proyek yang cukup besar dengan HTMX + Go, tetapi itu masih belum cukup, dan saya juga tidak yakin apakah ke depannya bisa mencapai tingkat yang saya inginkan. Untuk aplikasi CRUD sederhana dan dashboard admin ini luar biasa, tetapi ketika komponen yang saling terhubung, state bersama, dan interaksi yang kompleks mulai banyak, pengelolaannya cepat menjadi sulit. Saya memilih HTMX karena tidak suka React, tetapi pada akhirnya backend tetap di Go dan frontend saya ganti ke mode SPA SvelteKit; dengan memisahkan keduanya secara rapi, pengembangan dan pemeliharaan UI yang kompleks jadi jauh lebih mudah. Svelte terasa seperti perpanjangan alami dari HTML, bukan bahasa lain seperti JSX, model state management dan komponennya sederhana, dan sintaks $state yang baru juga sangat bagus.

    • Situs HTMX juga dengan jelas menyatakan trade-off bahwa itu memang tidak cocok untuk kegunaan seperti itu, tetapi untuk menemukan tulisan terkait kita harus menggali agak dalam.
    • Hyperscript, proyek saudara frontend dari tim HTMX, menangani fungsi seperti state frontend. Saya penasaran apakah ini juga sudah dicoba: https://hyperscript.org/docs/reactivity/
    • Inti dari filosofi HTMX tampaknya justru terletak pada mempertanyakan apakah komponen yang terhubung dengan state bersama benar-benar diperlukan sejak awal. Pada akhirnya, sebagian besar aplikasi hanya melakukan CRUD terhadap database.
  • Akhir-akhir ini hampir semua side project saya ditulis dengan Go + HTMX, terlepas dari ada atau tidaknya bantuan LLM. Opus dan GPT juga sangat mahir menangani kombinasi ini, bisa dibuat dan dimulai dengan cepat, dan karena berupa satu binary, deployment serta hosting juga praktis. Ini stack yang sangat bagus untuk pengembangan iteratif cepat.

  • Jika memakai HTMX, saya sangat menyarankan cara menghasilkan HTML di backend yang memudahkan kita mengekstrak potongan HTML bersama menjadi fungsi dan mengomponenkannya seperti di React. HTMX membutuhkan fleksibilitas pada HTML yang dihasilkan backend, misalnya dalam situasi tertentu kita perlu menambah atau menghapus tag tepat di samping HTML tertentu. Contohnya, jika di level teratas ditemukan tag ``, judul halaman akan diperbarui. Dengan template engine tradisional berbasis string, hal seperti ini sulit dilakukan, tetapi dengan library HTML embedded dalam bahasa hal itu mudah, dan karena kita bisa memanfaatkan seluruh kemampuan abstraksi bahasa, pendekatan ini sangat cocok dengan HTMX. Jika backend Anda JavaScript, Anda bisa memakai fungsi tagged template literal seperti https://github.com/WebReflection/uhtml-ssr, untuk Go ada https://www.gomponents.com/, dan untuk Scala ada ScalaTags.

  • Saya pertama kali belajar Go dari buku Alex Edwards dan Learn Go with Tests, dan sampai sekarang masih merekomendasikannya. Jadi ingin mencoba mengubah sebagian proyek lama ke HTMX.

    • Jika Anda punya minat sekecil apa pun pada pengembangan web Go, Anda harus membaca Let's Go dan Let's Go Further karya Alex Edwards. Itu adalah buku pengantar bahasa pemrograman yang paling mudah diakses, menyenangkan, dan praktis yang pernah saya baca.
  • Saya membuat framework berbasis Kotlin + HTMX untuk penggunaan pribadi: https://github.com/reubenfirmin/zoned Tujuannya adalah melihat apakah seluruh web app bisa dibuat type-safe secara end-to-end, dan saya menggunakan Kotlinx.html yang menulis HTML dengan DSL mirip JSX. 90% pertama saya buat sendiri, dan untuk 10% terakhir serta README saya dibantu Claude agar bisa menuntaskan proses rilis publik.

  • Saya suka Go dan Alex Edwards, tetapi hasil menggunakan HTMX selalu mengecewakan. Rasanya kompleksitas codebase tumbuh dua kali lebih cepat daripada aplikasi itu sendiri, dan saya selalu menemui edge case yang tidak bisa dihindari tanpa trik aneh. Saya paham alasan orang tidak suka package Node, tetapi HTMX terlihat seperti kompensasi berlebihan untuk itu. Waktu yang dihemat karena tidak perlu bergulat dengan JSON kalah jauh dibanding waktu ekstra untuk membuat tampilan dan nuansa aplikasi terasa benar; biayanya terasa tiga kali lebih besar. Dengan template Mantine, dalam 2 menit kita bisa menyiapkan dan memanfaatkan komponen UI yang luar biasa, dan jika aset statis hasil build disertakan, hasilnya tetap bisa menjadi satu binary Go yang sama: https://github.com/mantinedev/vite-min-template

    • Saat pertama kali mengenal hypermedia lewat Rails, semuanya terasa praktis dan mudah, jadi friksi di Go/HTMX cukup mengejutkan, tetapi setelah mencoba berkali-kali saya belajar bahwa abstraksi itu penting. Tanpa pembuat komponen dan reverse router, ini cukup menyakitkan, sedangkan Rails menyediakan itu secara bawaan sehingga kita tidak perlu memikirkannya. Keuntungan terbesarnya adalah komputasi dan data berada di satu tempat. LLM juga bagus dalam menggunakan dan menguji aplikasi hypermedia yang self-contained, dan siklus iterasi lebih singkat karena tidak perlu menunggu JavaScript. Namun saya setuju bahwa sulit untuk mulai cepat karena tidak ada library komponen yang bisa langsung dipakai.
    • Untuk memakai HTMX, saya harus mengubah seluruh filosofi desain. Ini belum tentu kekurangan, bahkan bisa jadi perubahan yang baik, tetapi tetap merupakan pergeseran besar, dan hasil akhirnya lebih dekat ke bentuk web yang khas daripada meniru aplikasi mobile.
    • Saya penasaran contoh UI spesifik apa yang bisa dibuat dengan Mantine tetapi tidak bisa dengan HTMX.
  • Saya jauh lebih menyukai Go + Datastar karena lebih sederhana.

    • Go dan Datastar cocok sekali satu sama lain, seperti sarung tangan yang pas. Saya juga ingin menambahkan a-h/templ ke template, dan Datastar juga mendukungnya. Mengejutkan bahwa Datastar masih belum sepopuler itu.
    • Saya juga lebih suka Datastar, tetapi fitur baru HTMX4 banyak memperkecil jaraknya. Menyenangkan melihat ekosistem mulai mengerucut ke pola-pola yang baik.