- Go के
html/template पर आधारित server-side rendering को बनाए रखते हुए HTMX से dynamic interactions जोड़ता है, और एक ही renderer से पूरी page और HTML fragments को चुनिंदा रूप से लौटाता है
base.tmpl, page templates और reusable fragments में markup को बांटता है, और Go 1.16 के embed.FS और shared template set का उपयोग करके deployment और rendering structure को सरल बनाता है
HX-Request के आधार पर search results को पूरी page या table rows के रूप में लौटाता है, और Vary: HX-Request तथा historyRestoreAsHxRequest: false से cache और back navigation restore errors को रोकता है
- HTMX request को किसी दूसरी page पर ले जाते समय सामान्य 3xx के बजाय
HX-Redirect और 2xx response का उपयोग करता है, और 204·422·4xx·5xx के हिसाब से responseHandling के जरिए replacement scope और error display को नियंत्रित करता है
- Local storage cache और attribute inheritance को बंद करके, और request timeout सेट करके configuration को शुरुआती बिंदु बनाता है; scale बढ़ने पर intermediate layout templates जोड़कर admin area जैसी अलग screen structures तक support किया जा सकता है
Go की server rendering और HTMX को मिलाने की वजह
- HTMX कम JavaScript के साथ dynamic interactions जोड़ते हुए Go
html/template की server-side HTML rendering से मिलने वाली consistency और safety बनाए रखने देता है
- implementation में तीन पहलुओं को संभाला गया है
- पूरी page और partial HTML responses, दोनों को support करने वाला template structure
- HTMX environment में redirects और error handling
- HTMX defaults को बदलने वाली configuration और उसके कारण
- button को image से replace करने वाली basic functionality से शुरू करके, username या email टाइप करते समय list filter करने वाली search screen तक विस्तार करता है
- यही template pattern सिर्फ HTMX ही नहीं, बल्कि Unpoly और Hotwire जैसे HTML-over-the-wire tools पर भी लागू हो सकता है
- अगर HTMX की basic usage नहीं जानते, तो पहले official documentation देखना बेहतर है
Project directory और static file configuration
- project को role के अनुसार इन areas में बांटा गया है
assets/html/base.tmpl: सभी pages द्वारा share किया जाने वाला HTML skeleton
assets/html/pages/: individual page content
assets/html/partials/: कई जगहों पर reuse होने वाले HTML fragments
assets/static/css, assets/static/img, assets/static/js: static assets
cmd/web/: server startup, handlers, HTML renderer code
- basic skeleton इन commands से generate करता है
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 को CDN या NPM से भी install किया जा सकता है, लेकिन आम तौर पर copy download करके application की static file के रूप में सीधे serve करने का तरीका इस्तेमाल किया जाता है
- configuration सरल रहती है और CDN use करने से जुड़ी कमियों से बचा जा सकता है
htmx.org@2.0.10 और class-less CSS framework 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
Base, page और partial templates को अलग करना
base.tmpl पूरे document skeleton जैसे <head>, common title, <main> को define करता है और page:title तथा page:content insert करता है
- Bamboo CSS और HTMX script को base template में load किया जाता है, और HTMX script पर
defer attribute लगाया जाता है
- browser HTML parse करते समय script को parallel में fetch करता है
- HTML parsing और DOM construction पूरी होने के बाद script execute करता है
- सभी templates file name पर निर्भर नहीं रहते, बल्कि
{{define}}...{{end}} से explicit name दिए जाते हैं
- Go code defined names के जरिए templates को consistently refer कर सकता है
page:title में : एक arbitrary separator है; page_title, page-title, pageTitle, title जैसे names भी संभव हैं
- homepage button में दो actions specify किए गए हैं
hx-get="/gopher": click पर GET /gopher request भेजता है
hx-swap="outerHTML": response HTML से button itself को replace करता है
partial:image:gopher reusable image fragment है, और width="{{.}}" के जरिए call time पर image width receive करता है
- किसी particular page में ही use होने वाले HTML fragments को common
partials directory के बजाय उसी page file में रखा जा सकता है
- कई pages में use होने वाले fragments common directory में, और page-specific fragments संबंधित content के पास रखने का यह distinction है
Go binary में HTML और static assets शामिल करना
- Go 1.16 में file embedding जुड़ने के बाद, runtime पर disk से पढ़ने के बजाय HTML और static assets को Go binary में शामिल किया जा सकता है
//go:embed "html" "static" directive से दोनों directories को embed.FS में डालता है, और fs.Sub() का उपयोग करके उन्हें दो sub file systems में अलग करता है
HTMLFiles: html को root के रूप में use करने वाला template file system
StaticFiles: static को root के रूप में use करने वाला static file system
- अलग करने से दो फायदे मिलते हैं
- HTML code और static file code एक-दूसरे की files तक अनावश्यक रूप से access नहीं करते
- file खोलते समय
html/ या static/ prefix लगाने की जरूरत नहीं होती
- अगर
sub() में panic() use नहीं करना है, तो error return करके main() में दोनों variables initialize कर सकते हैं
- current implementation में
fs.Sub() fail होने के लिए directory value को invalid path होना पड़ेगा, लेकिन fixed strings "html" और "static" यह check pass कर जाती हैं, इसलिए runtime panic का risk बहुत कम है
Shared template set और htmlRenderer
htmlRenderer rendering flow को इस तरह manage करता है
- start पर shared template set को एक बार parse करता है
- हर render के समय shared set को clone करता है
- request के लिए जरूरी page template को अतिरिक्त रूप से parse करता है
- specified name का template execute करके HTTP response के रूप में भेजता है
newHTMLRenderer() template.FuncMap register करने के बाद common templates parse करता है
now को time.Now से connect करता है
- दूसरी custom template functions भी इसी जगह add की जा सकती हैं
render() sharedTemplates.Clone() से per-request template set बनाता है, और additionalTemplateFiles हों तो ParseFS() से expand करता है
- template को पहले
bytes.Buffer में execute किया जाता है, फिर status code record करके response body के रूप में भेजा जाता है
- server initialization के समय
"base.tmpl" और "partials/*.tmpl" को shared set के रूप में parse करता है
- base template और सभी common fragments renderer में हमेशा available रहते हैं
- page-specific templates सिर्फ handler call के समय add करता है
- static files को
http.FileServerFS(assets.StaticFiles) और /static/ path से serve किया जाता है, और server :5051 पर चलता है
पूरी page और HTML fragments render करना
- homepage handler इस call से पूरा document लौटाता है
app.html.render(w, 200, nil, "base", "pages/home.tmpl")
- यह call shared set में
pages/home.tmpl जोड़कर base template को 200 OK के साथ execute करता है
/gopher handler कोई additional file parse नहीं करता, और shared set में पहले से शामिल केवल partial:image:gopher execute करता है
width := 100
app.html.render(w, http.StatusOK, width, "partial:image:gopher")
- HTMX
/gopher से मिली image को hx-swap="outerHTML" सेटिंग के अनुसार button से बदल देता है
- यह structure ये फायदे देता है
- templates और static assets binary में शामिल हो जाते हैं, जिससे deployment आसान हो जाता है
- उसी
render() से पूरा HTML page और कोई खास HTML fragment, दोनों return किए जा सकते हैं
- base template और partial templates से markup duplication कम होता है
- partial templates को base template, page content और दूसरे partial templates में भी reuse किया जा सकता है
input करते ही update होने वाला user search
- search feature दो routes में बंटा है
GET /users: सभी users की जानकारी वाला पूरा HTML page
GET /users/search: सिर्फ उन users की table rows वाला HTML fragment जिनका नाम या email search term से match करता है
- search input field इन HTMX attributes को मिलाकर इस्तेमाल करता है
hx-get="/users/search" से search request भेजता है
hx-trigger="input changed delay:500ms, keyup[key=='Enter']" से input बदलने के 500ms बाद या Enter दबाने पर request करता है
- search term
GET /users/search?query=foo जैसे query string के रूप में भेजा जाता है
hx-target="#search-results" से response को <tbody id="search-results"> के अंदर डालता है
hx-push-url="true" से हर request पर address bar update करता है और browser history में entry जोड़ता है
users:rows एक अलग template है जो सिर्फ user rows render करता है
- हर row में नाम और email दिखाया जाता है
- अगर
IsGopher true हो, तो partial:image:gopher को width 24 के साथ render करता है
listUsers और searchUsers दोनों shared set में pages/users.tmpl जोड़ते हैं, लेकिन execute किया जाने वाला template अलग होता है
listUsers: base execute करके पूरा page return करता है
searchUsers: users:rows execute करके केवल matching rows लौटाता है
- अगर search term खाली हो, तो पूरी user list इस्तेमाल की जाती है; अगर value हो, तो
strings.Contains() से नाम और email check किए जाते हैं
- real application में ये improvements consider किए जा सकते हैं
- list और search handlers को एक में merge करना
- error handling और logging helpers को centralize करना
- middleware से Content Security Policy header और panic recovery जोड़ना
- server timeouts को सही तरीके से set करना
HX-Request से full page और partial response में अंतर करना
- अगर user
/users/search?query=leo को browser में सीधे खोलता है या shared link से visit करता है, तो सिर्फ table rows वाला partial HTML दिख सकता है
- HTMX requests में हमेशा
HX-Request: true header शामिल होता है, इसलिए इसी के आधार पर response form तय किया जाता है
func isHTMXRequest(r *http.Request) bool {
return r.Header.Get("HX-Request") == "true"
}
- search handler का default execution template
base रखा जाता है, और केवल HTMX request होने पर इसे users:rows में बदला जाता है
- सीधे visit करने पर search results वाला पूरा page मिलता है
- HTMX request में
<tbody> में डालने के लिए सिर्फ row fragment return होता है
Vary header और back navigation restore
- चूंकि वही URL
HX-Request value के अनुसार अलग-अलग response return करता है, cache को भी इस difference की जानकारी देनी चाहिए
- सभी rendering responses में
Vary: HX-Request जोड़ा जाता है
- strictly देखें तो individual handler में जरूरत पड़ने पर ही set करना कम wasteful है
- renderer में bulk में set करने से omission के कारण होने वाले bugs से बचा जा सकता है
- जब
hx-push-url या hx-boost browser history जोड़ता है, तो HTMX completed page HTML को browser local storage में cache करता है
- default cache अधिकतम 10 pages का होता है
- अगर cache में न मौजूद point तक back जाएं, तो HTMX server से उस URL को फिर request करता है
- default restore request में
HX-Request: true शामिल होता है, इसलिए server full page के बजाय partial HTML return कर सकता है
- अगर
HX-Request से response type चुन रहे हैं, तो historyRestoreAsHxRequest: false set करें
- cache miss restore request से
HX-Request: true हटाता है
- server उस request के लिए पूरा HTML page return करता है
- HTMX configuration को
base.tmpl के htmx-config meta tag में डाला जाता है
HTMX request को किसी दूसरे page पर redirect करना
- HTMX success message जैसे HTML fragments को तुरंत replace कर सकता है, इसलिए सामान्य Post/Redirect/Get flow की जरूरत कम हो जाती है
- login success के बाद profile पर जाने की तरह, जब पूरी तरह दूसरे page पर भेजना हो तो सिर्फ सामान्य 3xx response से handle नहीं किया जा सकता
- browser HTMX से पहले 3xx response को follow कर लेता है
- HTMX redirect के बाद के final response को ही देखता है और उसके content को existing target में replace कर देता है
- HTMX request में 2xx response के साथ
HX-Redirect header इस्तेमाल करें
HX-Redirect: /foo/bar browser को उस URL पर ले जाता है और पूरा page फिर load करता है
- destination request में
HX-Request: true शामिल नहीं होता
- अगर JavaScript बंद हो या HTMX load न हुआ हो, ऐसे environment को भी support करना है, तो non-HTMX requests के लिए सामान्य 3xx response बनाए रखना चाहिए
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)
}
- login के बाद
/profile पर जाने के लिए इसे इस तरह call कर सकते हैं
redirect(w, r, "/profile", http.StatusSeeOther)
HX-Redirect और HX-Location में अंतर
HX-Location पूरा page reload किए बिना redirect जैसा behavior करता है
- target URL का HTML fetch करता है
- response को
<body> में replace करता है
- browser history में नई entry जोड़ता है
- target URL request करते समय
HX-Request: true शामिल होता है
- अगर target path
HX-Request के अनुसार partial HTML return करता है, तो problem हो सकती है
- handler
HX-Location से आई request और normal HTMX request में अंतर नहीं कर सकता
- पहले के लिए full page चाहिए, लेकिन दूसरे के लिए partial response चाहिए
- history restore के विपरीत,
HX-Location navigation में HX-Request: true बंद करने की कोई setting नहीं है
- ज्यादातर मामलों में full page reload स्वीकार करके
HX-Redirect इस्तेमाल करना ज्यादा आसान और सुरक्षित है
- अगर destination path हमेशा केवल full HTML return करने के लिए configured है, तो
HX-Location भी इस्तेमाल किया जा सकता है
204·422·4xx·5xx responses की handling
- HTMX default रूप से 4xx या 5xx response को DOM में replace नहीं करता
- current screen को वैसा ही रखता है
- error developer tools console में log होता है
- किसी non-existent template name के कारण server
500 Internal Server Error return करे, तब भी default setting में user screen पर error नहीं देख पाता
- error message user को दिखाने के लिए 4xx और 5xx responses को पूरे
<body> में replace करने के लिए set करें
- exception
422 Unprocessable Content है
- इसे form validation errors वाला form return करने पर इस्तेमाल किया जाता है
- response existing HTMX target में normal तरीके से replace होना चाहिए
responseHandling को status code के अनुसार इस तरह configure करें
204 No Content: DOM नहीं बदलता
422 Unprocessable Content: existing target में normal replacement करता है
- बाकी 4xx और 5xx: response को
<body> में replace करता है
- अन्य responses: existing target में normal replacement करता है
"responseHandling":[
{"code":"204", "swap": false},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": true, "target": "body"},
{"code":"...", "swap": true}
]
- अगर किसी खास interaction में errors को
<body> के बजाय किसी दूसरे target में डालना हो, तो response target extension से default setting को override किया जा सकता है
ब्राउज़र address bar में मौजूदा URL की जाँच
- HTMX, अगर
hx-boost, hx-push-url, hx-replace-url का उपयोग न किया जाए, तो AJAX request और response replacement के दौरान browser URL नहीं बदलता
- इस वजह से Go handler का
r.URL और user को address bar में दिखने वाला URL अलग हो सकता है
- HTMX हर request के
HX-Current-URL header में वह URL भेजता है जो browser वर्तमान में दिखा रहा है
- helper function इस header को
url.URL में parse करता है, और अगर header नहीं है तो 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
}
HTMX defaults बदलने वाली configuration
historyCacheSize: 0 से HTMX का local storage page cache पूरी तरह बंद हो जाता है
- local storage cache bugs और security issues का स्रोत बन सकता है
- back button दबाने पर cache के बजाय server से HTML फिर से fetch किया जाता है
- भविष्य के HTMX versions में इसी वजह से local storage caching default रूप से disabled होगी
disableInheritance: true से HTMX attribute inheritance disabled हो जाता है
- attributes को हमेशा explicit रूप से declare करना ज्यादा स्पष्ट रहता है
- bugs या अनचाहे behavior का risk कम होता है
- भविष्य के HTMX versions में attribute inheritance भी default रूप से disabled होगा
includeIndicatorStyles: false से HTMX indicator styles inject नहीं करता
- consistency बनाए रखने के लिए indicator styles को दूसरी CSS rules के साथ खुद define करें
- HTMX requests में default रूप से कोई timeout नहीं होता, इसलिए वे server के respond करने तक wait करती हैं
- Go application में timeout और deadline handling के तरीके के अनुसार
timeout को milliseconds में specify किया जा सकता है
- शुरुआती point के रूप में
timeout: 5000 इस्तेमाल करें
- default configuration में
historyRestoreAsHxRequest: false और status code के हिसाब से responseHandling भी शामिल है
<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
}'
>
बड़े applications के लिए page-specific layout
- अगर basic template और page-specific template पर्याप्त न हों, तो इन दो layers के बीच layout template जोड़ा जा सकता है
- यह admin pages जैसे उन areas के लिए उपयुक्त है जिन्हें सामान्य pages से अलग title, navigation menu और body structure चाहिए
base, page:content को सीधे call करने के बजाय layout को call करता है
<body>
{{template "layout" .}}
</body>
layouts/admin.tmpl admin area की common structure define करता है और अंदर page:content को call करता है
- admin area title
- users और orders पर जाने वाला navigation menu
- page-specific content के लिए
<main>
- handler,
render() में उपयोग होने वाला layout और page template साथ में pass करता है
app.html.render(
w,
200,
nil,
"base",
"layouts/admin.tmpl",
"pages/admin-orders.tmpl",
)
- shared base template बनाए रखते हुए route-specific layouts और page content को combine किया जा सकता है, इसलिए admin area जैसी अलग structure वाली screens को भी उसी rendering pattern से extend किया जा सकता है
1 टिप्पणियां
Hacker News प्रतिक्रियाएँ
Go + HTMX पसंद है, और templates·components·partial HTML की type safety बढ़ाने के लिए इसे a-h/templ के साथ इस्तेमाल करते हैं अपने पूरे toolset को Go·Unix·SQLite से बनाकर इसे GUS stack नाम दिया है और सार्वजनिक किया है: https://housecat.com/blog/the-gus-stack-go-unix-sqlite exe.dev के GUTS stack से काफी प्रेरणा ली, लेकिन TypeScript की जगह HTMX इस्तेमाल किया: https://exe.dev/docs/guts stack trace errors के लिए cockroachdb/errors, type-safe HTML के लिए templ, OpenAPI spec generation के लिए fuego, SQL code generation के लिए sqlc, pure Go SQLite के लिए modernc.org/sqlite, migrations के लिए goose, durable workflow के लिए dbos, और Chrome/CDP testing व automation के लिए rod का उपयोग करते हैं यह संयोजन खुद coding करते समय या agent की मदद से coding करते समय, और single binary build·deploy करने की पूरी प्रक्रिया में उत्पादक साबित होता है
go generate ./...को build step के रूप में स्वीकार कर लें तो काफी फायदे मिलते हैं, और goverter से sqlc models, template objects और return values के बीच conversion भी generate किया जाता है लगभग 50% boilerplate auto-generate हो जाता है, जिससे type safety बहुत अच्छी हो जाती हैHTMX कई कामों के लिए बेहतरीन है, लेकिन अगर टीम के साथी सहमत न हों तो इसे अपनाना बहुत मुश्किल होता है यह कोई serious technology नहीं है जैसी प्रतिक्रिया मिलती रहती है, और तरह-तरह के bugs का दोष पहले HTMX पर डाल दिया जाता है; बाद में गलतफ़हमी साबित भी हो जाए तो भरोसा पहले ही टूट चुका होता है अब तक जिन टीमों में काम किया, उनमें सबसे बेहतरीन टीम में भी ऐसा हुआ, और आखिरकार यही सीख मिली कि किस लड़ाई को लड़ना है, यह चुनना पड़ता है Go का html/template भी असामान्य रूप से बेढंगा interface रखता है। 《A Philosophy of Software Design》 की सलाह के मुताबिक complexity को भीतर धकेलना चाहिए, लेकिन हर बार HTML render करते समय template cloning की चिंता करनी पड़े, ऐसी संरचना अनावश्यक रूप से जटिल लगती है
base.tmplकोBASE_BEGINऔरBASE_ENDमें बाँटकर final template में इस्तेमाल करें page-specific title runtime पर pass करना पड़ेगा, लेकिन मूल तरीका भी multilingual support शुरू होते ही आखिरकार टूट जाएगाहाल की project में HTMX का उपयोग करके बहुत आनंद आया AngularJS और React से पहले के web को जानने वाले के रूप में, असली pages बना पाना और JavaScript की मात्रा को न्यूनतम रखना खास तौर पर अच्छा लगा pure JavaScript से भी यह संभव है, लेकिन HTMX दोहराए जाने वाले event-handling boilerplate को अपने ऊपर ले लेता है अगर आपको modern frontend philosophy पसंद नहीं है, तो इसे आज़माना चाहिए; official site के शुरुआती examples बहुत बुनियादी हैं, लेकिन थोड़ा और सीखें तो यह कहीं ज़्यादा शक्तिशाली है
मैंने काफ़ी बड़ा प्रोजेक्ट HTMX + Go से बनाकर देखा, लेकिन यह अभी भी पर्याप्त नहीं लगा, और आगे चलकर यह मेरी मनचाही सीमा तक पहुँचेगा या नहीं, इस पर भी भरोसा नहीं है
साधारण CRUD ऐप्स और admin dashboard के लिए यह शानदार है, लेकिन जैसे-जैसे आपस में जुड़े components, shared state, और complex interactions बढ़ते हैं, इसे संभालना तेज़ी से मुश्किल हो जाता है
React पसंद न होने की वजह से HTMX चुना था, लेकिन अंत में backend को Go में ही रखा और frontend को SvelteKit SPA मोड में बदल दिया; दोनों पक्षों को साफ़-सुथरे ढंग से अलग करते हुए complex UI को काफ़ी आसान तरीके से develop और maintain कर पाया
Svelte, JSX जैसी किसी अलग language से ज़्यादा HTML के natural extension जैसा लगता है, और इसका state management व component model सरल है; नया
$statesyntax भी ख़ास तौर पर अच्छा लगाआख़िरकार ज़्यादातर apps database पर CRUD ही करते हैं
आजकल मेरे side projects लगभग सभी Go + HTMX में लिखे जाते हैं, चाहे LLM की मदद हो या न हो
Opus और GPT दोनों इस संयोजन को बहुत अच्छे से संभालते हैं, इससे जल्दी बनाना और शुरू करना आसान होता है, और single binary होने की वजह से deployment व hosting भी सुविधाजनक है
तेज़ iteration के लिए यह बहुत अच्छा stack है
अगर HTMX इस्तेमाल कर रहे हैं, तो backend में भी React की तरह common HTML fragments को functions में निकालकर components की तरह इस्तेमाल करने वाली HTML generation शैली को मैं ज़ोरदार तरीके से recommend करूँगा
HTMX में context के हिसाब से किसी खास HTML के ठीक पास tags जोड़ने या हटाने पड़ सकते हैं, यानी backend द्वारा generate किए गए HTML में flexibility चाहिए। उदाहरण के लिए, top-level पर `` tag मिल जाए तो page title update हो जाता है
पारंपरिक string-based template engine में यह काम मुश्किल होता है, लेकिन language-embedded HTML libraries में यह आसान है, और क्योंकि आप पूरी language की abstraction capabilities का उपयोग कर सकते हैं, इसलिए यह HTMX के साथ ख़ास तौर पर अच्छी तरह फिट बैठता है
JavaScript backend में https://github.com/WebReflection/uhtml-ssr जैसी tagged template literal functions, Go में https://www.gomponents.com/, और Scala में ScalaTags इस्तेमाल किए जा सकते हैं
अगर dependency-free HTML tagged template literal library चाहिए, तो यह implementation और docs देखना उपयोगी हो सकता है
implementation: https://github.com/mastrojs/mastro/blob/main/src/core/html.t...
docs: https://mastrojs.github.io/docs/html-components/
React में
renderToStaticMarkupहै, लेकिन backend-only के लिए लिखी गई JSX library चुनना शायद बेहतर होगामैंने Go पहली बार Alex Edwards की किताबों और Learn Go with Tests से सीखा था, और आज भी उन्हें recommend करता हूँ
पुराने प्रोजेक्ट्स के कुछ हिस्सों को HTMX में migrate करके देखने का मन होता है
ये अब तक पढ़ी गई programming language introductions में सबसे approachable, आनंददायक और practical किताबों में हैं
मैंने निजी उपयोग के लिए Kotlin + HTMX आधारित framework बनाया है: https://github.com/reubenfirmin/zoned
लक्ष्य यह देखना था कि क्या पूरे web app को end-to-end type-safe बनाया जा सकता है, और HTML लिखने के लिए JSX-जैसे DSL वाली Kotlinx.html का उपयोग किया
शुरुआती 90% मैंने खुद बनाया, और आख़िरी 10% व README को public release तक पहुँचाने के लिए Claude की मदद ली
मुझे Go और Alex Edwards दोनों पसंद हैं, लेकिन HTMX इस्तेमाल करने का नतीजा हमेशा निराशाजनक रहा
ऐसा लगता है कि codebase की complexity, app की अपनी complexity से दोगुनी रफ़्तार से बढ़ती है, और हमेशा ऐसे edge cases मिलते हैं जहाँ अजीब workarounds के बिना निकलना संभव नहीं होता
समझ सकता हूँ कि लोग Node packages से क्यों नफ़रत करते हैं, लेकिन HTMX उसका overcorrection जैसा लगता है। JSON से जूझने में जो समय बचता है, उससे 3 गुना ज़्यादा समय app के look and feel को ठीक बनाने में लग जाता है
Mantine template को 2 मिनट में सेटअप करके बेहतरीन UI components का लाभ लिया जा सकता है, और built static assets शामिल कर दें तो इसे भी वैसा ही single Go binary बनाया जा सकता है: https://github.com/mantinedev/vite-min-template
component generators और reverse routers के बिना काफ़ी दर्द होता है, जबकि Rails यह सब by default देता है, इसलिए इस बारे में सोचना नहीं पड़ता
सबसे बड़ा फ़ायदा यह है कि computation और data एक ही जगह होते हैं। LLM भी self-contained hypermedia apps को अच्छे से इस्तेमाल और test कर लेते हैं, और JavaScript का इंतज़ार न करना पड़े तो iteration cycle छोटी हो जाती है
लेकिन सीधे उठाकर इस्तेमाल करने लायक component library न होने की वजह से शुरुआत करना मुश्किल है, इस बात से मैं सहमत हूँ
यह ज़रूरी नहीं कि कमी ही हो; यह अच्छा बदलाव भी हो सकता है, लेकिन यह काफ़ी बड़ा परिवर्तन है, और अंतिम नतीजा mobile app की नकल करने के बजाय web की अपनी प्रकृति के ज़्यादा करीब होता है
Go + Datastar मुझे ज़्यादा सरल लगता है, इसलिए मैं इसे काफ़ी ज़्यादा पसंद करता हूँ
templates में a-h/templ भी जोड़ना चाहूँगा, क्योंकि Datastar उसका भी समर्थन करता है
यह हैरानी की बात है कि Datastar अभी भी इतना कम जाना जाता है
ecosystem को अच्छे patterns की ओर converge करते देखना सुखद है