Bookmark List Page

Server-rendered HTML page with form handling using html/template.

package main

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

type Bookmark struct {
	ID    int
	URL   string
	Title string
}

var bookmarks = []Bookmark{
	{ID: 1, URL: "https://go.dev", Title: "Go"},
	{ID: 2, URL: "https://pkg.go.dev", Title: "Go Packages"},
}
var nextID = 3

const listTmpl = `<!DOCTYPE html>
<html>
<head><title>Bookmarks</title></head>
<body>
  <h1>Bookmarks ({{len .}})</h1>
  <form method="POST" action="/bookmarks">
    <input name="title" placeholder="Title" required>
    <input name="url" type="url" placeholder="https://example.com" required>
    <button type="submit">Add</button>
  </form>
  <ul>
  {{range .}}
    <li>
      <a href="{{.URL}}">{{.Title}}</a>
      <form method="POST" action="/bookmarks/{{.ID}}/delete" style="display:inline">
        <button type="submit">✕</button>
      </form>
    </li>
  {{else}}
    <li>No bookmarks yet.</li>
  {{end}}
  </ul>
</body>
</html>`

var tmpl = template.Must(template.New("list").Parse(listTmpl))

func listPage(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	tmpl.Execute(w, bookmarks)
}

func createBookmark(w http.ResponseWriter, r *http.Request) {
	title := r.FormValue("title")
	url := r.FormValue("url")
	if title != "" && url != "" {
		bookmarks = append(bookmarks, Bookmark{ID: nextID, URL: url, Title: title})
		nextID++
	}
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func deleteBookmark(w http.ResponseWriter, r *http.Request) {
	id, _ := strconv.Atoi(r.PathValue("id"))
	for i, b := range bookmarks {
		if b.ID == id {
			bookmarks = append(bookmarks[:i], bookmarks[i+1:]...)
			break
		}
	}
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /", listPage)
	mux.HandleFunc("POST /bookmarks", createBookmark)
	mux.HandleFunc("POST /bookmarks/{id}/delete", deleteBookmark)
	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy