Go embed: Embed your HTML frontend in Golang

How to embed HTML files in Go (build and run in multiple locations)

Table of contents

No heading

No headings in the article.

Parsing HTML files into Go can be easy using the "html/template" package until the main file is moved somewhere else or errors arise because you did not use containers (or docker) when hosting the application. In Go 1.16, a package called embed was introduced and it serves many purposes but mainly: embedding "anything" - strings, bytes, even files. This article explains how to use the embed package to insert HTML files into your golang application, build the golang app into an executable(.exe) file, and run that .exe file anywhere on your system without "HTML file not found" errors popping up.
Pre-requisites:

  • Golang installed on your system
  • basic knowledge of Golang

    The finished project can be found here: finished project
    Setup the project:
  • create a folder in your $GOPATH and name it go-embed
  • cd into go-embed and run: go mod init go-embed`
  • create the main.go inside the go-embed folder
  • create a folder templates, cd into templates and create the index.html file
    go-embed-1.png

Updating the files:
For the index.html, we display a simple message.
index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Go Embed</title>
</head>

<body>
  <h4>Go Embed: Embed HTML in Go</h4>
</body>

</html>


For the main.go file, we serve the HTML. I am using the default "net/http" package but you can use any package of your choice e.g. the "gorilla/mux" package.
main.go:

package main

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

func indexHandler(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("templates/index.html") // serving the index.html file
    if err != nil {
        log.Fatal(err)
    }
    tmpl.Execute(w, nil)
}

func main() {
    http.HandleFunc("/", indexHandler)

    fmt.Println("Server starting on port 8000!")
    err := http.ListenAndServe(":8000", nil)
    if err != nil {
        log.Fatal(err)
    }
}


Run the application: go run main.go
go-embed-2.png Open our app on the browser:

go-embed-3.png

Using the embed package:
In this section, we would embed the HTML file(index.html) in the Go binary which allows us to send the binary file anywhere and run that file without displaying any error.
Updating main.go:

package main

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

//go:embed templates
var tplFolder embed.FS // embeds the templates folder into variable tplFolder

func indexHandler(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFS(tplFolder, "templates/index.html")
    if err != nil {
        log.Fatal(err)
    }
    println("Embedded the index.html file")
    tmpl.Execute(w, nil)
}

func main() {
    http.HandleFunc("/", indexHandler)

    fmt.Println("Server starting on port 8000!")
    err := http.ListenAndServe(":8000", nil)
    if err != nil {
        log.Fatal(err)
    }
}


Run the application: go run main.go
go-embed-4.png

Refresh the browser page:
go-embed-5.png Check the running application server:
go-embed-6.png We do not have any error, instead we have successfully embedded index.html.

Building the file:
First we stop the app server running.
Build the file by running: go build
We have a binary application:
go-embed-7.png Move the binary file somewhere else and run it: ./go-embed for Linux/Mac or go-embed for Windows.
go-embed-8.png

Refresh the browser page:
go-embed-9.png Our app server:
go-embed-10.png Where can this embed feature be applied?
An example: Github
Here, I embedded all the HTML files which allow me to perform the authentication (registration or login) anywhere my application is even though the HTML files are not present. This makes hosting the application on Heroku or any hosting platform easier without getting file errors.