How to write a ToDo App with HTMX in Go

published on 26 January 2024

HTMX and Golang

In the world of web development, simplicity and efficiency are key. This is where the combination of HTMX and Go comes into play. HTMX allows you to access AJAX, CSS Transitions, WebSockets, and more, directly in HTML, making your front-end development simpler. Golang, on the other hand, is renowned for its efficiency and simplicity in backend development. In this blog post, we will create a basic Todo application using HTMX on the frontend and Go as the backend server.

Setting Up the Go Environment

First, ensure you have Go installed on your system. You can download it from the official Go website.

Once installed, create a new directory for our project and initialize a new Go module:

mkdir todo-app
cd todo-app
go mod init todo-app

Creating the Go Server

Our Golang server will handle HTTP requests. Let's start by creating a file named main.go:

package main

import (
    "html/template"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        tmpl, err := template.ParseFiles("index.html")
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        tmpl.Execute(w, nil)
    })

    log.Println("Starting server on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

This code snippet creates a basic web server that listens on port 8080 and serves an index.html file.

Integrating HTMX

HTMX is a powerful tool that allows you to access modern browser features directly from HTML. To use HTMX, you need to include it in your HTML file. Create a file named index.html in the same directory:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Todo App</title>
    <script src="https://unpkg.com/htmx.org"></script>
</head>
<body>
    <h1>Todo App</h1>
    <!-- Todo List will be loaded here -->
    <div id="todo-list" hx-get="/todos" hx-trigger="load">
        Loading todos...
    </div>
</body>
</html>

This HTML file includes HTMX and sets up a div that will be populated with our Todo list.

Building the Todo List

Let's add the ability to list, add, and delete todos. We will start by defining a Todo struct in our Golang code:

type Todo struct {
    ID    int
    Title string
    Done  bool
}

For simplicity, we will store our todos in memory:

var todos = []Todo{
    {1, "Learn Go", false},
    {2, "Build a Todo App", false},
}

Now, let's create handlers for listing, adding, and deleting todos in main.go:

List Todos

http.HandleFunc("/todos", func(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("todos.html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    tmpl.Execute(w, todos)
})

Add Todo

http.HandleFunc("/add-todo", func(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        title := r.FormValue("title")
        todos = append(todos, Todo{len(todos) + 1, title, false})
        http.Redirect(w, r, "/", http.StatusFound)
    }
})

Delete Todo

http.HandleFunc("/delete-todo", func(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        id, err := strconv.Atoi(r.FormValue("id"))
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }
        // Delete todo from todos slice
        // ...
        http.Redirect(w, r, "/", http.StatusFound)
    }
})

The Frontend with HTMX

In index.html, use HTMX to handle adding and deleting todos:

<body>
    <!-- ... -->
    <form hx-post="/add-todo" hx-target="#todo-list">
        <input type="text" name="title" required>
        <button type="submit">Add Todo</button>
    </form>

    <div id="todo-list" hx-get="/todos" hx-trigger="load">
        <!-- Todos will be loaded here -->
    </div>
</body>

Each todo item can have a delete button:

<!-- In todos.html -->
{{range .}}
    <div>
        <span>{{.Title}}</span>
        <form hx-post="/delete-todo" hx-target="#todo-list">
            <input type="hidden" name="id" value="{{.ID}}">
            <button type="submit">Delete</button>
        </form>
    </div>
{{end}}

Conclusion

We've created a basic Todo application using HTMX and Go. This setup demonstrates how you can build dynamic and efficient web applications with minimal JavaScript, leveraging the power of HTMX on the frontend and the simplicity and performance of Go on the backend.

Remember, this is just a basic example. For a production application, you would need to consider aspects like persistent storage, input validation, and error handling in more depth. However, this example serves as a great starting point for building simple, dynamic web applications with these powerful technologies.

Read more