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
static
as a directory and try to serve it /static/
will get directed to a route../static
which 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-add
route in the Go serverhx-target="#books-list"
will update the#books-list
element with the response from the server. The response is in the form of HTML and will be added to the#books-list
element.hx-swap="beforeend"
will add the response to the end of the#books-list
element. 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
post
request - 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
htmx
is 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.