I have not been a fan of server-driven frontend experiences, but HTMX renaissance has piqued my interest. And yes, that only gets amplified with BunJS claims of astronomical speed for server and what it means for my choice of technologies moving forward. That is for a future post, but here we explore how Golang and HTMX can work together to create a “SPA-like” experience.
Features that are of interest here -
- Go and Go templates
- How to get HTMX working with Go templates
- How will static files co-exist with the dynamic behavior of HTMX (well, this is just me)
What is not considered -
- Authentication / user handling and all the good stuff
- Database and persistence of books
Install
Download Go and click on button and tab to install Go on your machine.
HTMX needs no install. We will just reference it with a CDN in the HTML.
Install nodemon globally since Go’s go run does not watch for changes.
|
|
If you are stuck anywhere while writing code, refer to the repository on Github.
Create a New Project
Create a new project folder and initialize a Go module.
|
|
Your go.mod file should look like this:
|
|
Create a simple server with a new file called main.go.
|
|
You may have noted that you don’t particularly need to type in the import.
Run the server with go run main.go and visit http://localhost:8080/hello to see the output.
It is time to reload server automatically when any changes are made to files.
|
|
Create a nodemon.json file to specify which folders need to be watched.
|
|
If you are into auto formatting using prettier and I don’t see why you shouldn’t, add prettier to work smoothly with Go and Go templates:
|
|
Modularizing the Code and Adding HTML
We don’t want to stuff everything in main.go. Let us create a folder pages and serve all pages from there.
But before creating the HTML page, we want to make sure things work.
Create a new file pages/hello.go and add the following code:
|
|
Update main.go to use the new handler.
|
|
This works as expected. Now, let us create a new file pages/hello.html and add the following code:
|
|
You should now see the glorious HTML page with a form and a sample book.
![]()
You will notice a couple of things off the bat:
- chota.css for styling. It is a tiny CSS framework that I like to use for simple projects and demos. You can use any CSS framework of your choice.
- Custom CSS referenced in the html page does not exist, we will get to that in a bit.
- The form does not do anything. We will get to that in a bit as well.
Serving Static Assets
We need to serve static files like CSS, JS, and images. Create a new folder static and add a new file static/css/main.css with the following code:
|
|
Add the below line to main.go.
|
|
The http.FileServer part is evident. http.StripPrefix(...) is needed to remove the /static/ prefix from the URL since -
- Go http will see
staticas a directory and try to serve it /static/will get directed to a route../staticwhich does not exist
Production sites often have static files or ready content to serve. We will take an example of help page to demonstrate. Create a new file pages/help.html and add any sample content.
|
|
Using Go Templates
With Go templates we separate the content from the presentation.
Add below code to main.go -
|
|
books is a just a map of strings to slice of Book struct. We will refer Books within the map in html to render the books in the HTML page. In real world, books will come from a database query.
Replace the hard-coded books in index.html to -
|
|
You will now see books on the site, but the information is coming from the Go server.
All of this is good but it provides a fairly static experience. Importantly, we have not added any functionality to add books.
Enter HTMX.
Add HTMX
Add the following script to the bottom of the head tag in index.html.
|
|
Modify the form to add hx-post and hx-target attributes.
|
|
You will see that -
hx-post="book-add"is meant to call the/book-addroute in the Go serverhx-target="#books-list"will update the#books-listelement with the response from the server. The response is in the form of HTML and will be added to the#books-listelement.hx-swap="beforeend"will add the response to the end of the#books-listelement. We are not replacing the entire book list with the server response, rather just add the new book at the very end of the list. See more on these methods in HTMX docs- We reset form on successful submission with
hx-on::after-request=" if(event.detail.successful) this.reset()
Add a template reference in the book data rendering code -
|
|
The only change you see is the {{ block "book-item" . }} reference. We have used block to create a template reference. Go code will refer to this template reference so that you can just pass in the data and rest of the HTML + CSS will be reused from the HTML file. There are of course more mature ways of handling this in real-world apps including creation of separate template files for individual components.
Add the following code to main.go to handle the book-add request.
|
|
Here -
- We just receive the
postrequest - Parse and update the content
We would have written to a database in real-world apps.
That is it. You have a working app with HTMX and Go, which does not need any refreshes and behaves like a single page app.
See the complete code is on Github.
Conclusion
- The simplicity of
htmxis awesome. It is a great way to add interactivity to the page without having to deal with JavaScript. - HTMX will reduce a lot of code that we have all come to write and love(?)
- Javascript will not go anywhere. I find it more intuitive to write JS than Go templates + HTMX (or any other template engine for that matter).
- If you take the Javascript part out of the equation, writing future web apps in languages like Go, C#, etc. may in fact be enjoyable.
While I continue to not be a fan of server-driven frontend and templates (like Go templates, Pug), I am excited to see how htmx will evolve and what it means for the future of frontend development.