Setup HTML Templates and Static Assets in Golang

Published on Friday, 29 December 2023

This article is a continuation of the previous one, where we built a simple Todo App backend with Golang.

This guide focuses on generating HTML templates, serving static CSS and Javascript files in Go, setting up the Fronted app and connecting it to the backend.

As we've finished building the backend application, it's time to set up the front end, connect it to the backend, and create a full-stack web application.

This article lays the foundation for the upcoming one, where we will build a Todo Frontend App with the Fetch API and Vanilla JavaScript (with a TypeScript version available).

This Todo App has monolith architecture, indicating the Frontend and Backend share the same codebase.

We are using Go to create a simple web server service that will serve HTML, CSS and JavaScript files when you navigate to a localhost address.

Render HTML template in Go.

To render HTML templates in a Go application, we first need to parse them. Parsing means that Go can read the HTML template file we create, identify syntax and placeholder tokens, and then render the file as an HTML file.

The renderer package , which we use for handling API responses in the Golang backend todo app, provides a straightforward way to parse HTML templates. Under the hood, it uses the Go html/template package. However, you can use the default Go html/template package, depending on your preference.

ADD HTML Template

The HTML template contains code for a task input box and a submit button. The div with id "todos" is used to add todos retrieved from the backend API and display them dynamically in the todos div on the Frontend.

input-box.jpg

To add the HTML template,

  1. Create an “html” folder in your project’s root directory.
  2. Inside the folder, create an “index.html” file.
  3. Copy and paste the following code into the "index.html" file, or you can download the file from GitHub.
1{{define "indexPage"}}
2<!DOCTYPE html>
3<html lang="en">
4  <head>
5    <meta charset="UTF-8" />
6    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7    <title>To Do App - with html, css and Javascript</title>
8    <link rel="preconnect" href="https://fonts.googleapis.com" />
9    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10    <link
11      href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap"
12      rel="stylesheet"
13    />
14    <link
15      rel="stylesheet"
16      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"
17    />
18
19    <link rel="stylesheet" type="text/css" href="/static/style.css" />
20  </head>
21
22  <body>
23    <div class="container">
24      <div id="new-todo">
25        <input type="text" placeholder="Tasks to be done" />
26        <button type="button" id="submit">Add</button>
27      </div>
28      <div id="todos"></div>
29    </div>
30  </body>
31</html>
32{{end}}
33

In the code above, we define an HTML template named” indexPage”, which we will serve as the Response when a user navigates to our homepage.

We linked the CSS file to the index page, ensuring the HTML templates applied the styles correctly.

Add CSS File

To add the CSS file;

  1. Create a Static folder in the root directory. You can name this folder “public” or “assets” if you want.
  2. Inside the folder, create “style.css” file.
  3. Copy and paste the following code into the "style.css" file, or you can download the file from GitHub. (link to file on GitHub).
  4. Link the css file to “index.html” file. Make sure you have added the css link to the <head> tag in the index.html file
1<head>
2  ...
3  <link rel="stylesheet" type="text/css" href="/static/style.css" />
4</head

This is a basic styling I created for the Todo app frontend layout. Feel free to use this or the styles of your choice.

static/style.css

1*,
2*::before,
3*::after {
4  padding: 0;
5  margin: 0;
6  box-sizing: border-box;
7}
8
9body {
10  height: 100vh;
11  background: linear-gradient(135deg, #8052ec, #d161ff);
12}
13
14.container{
15 border: 2px solid white;
16 width: 40%;
17 min-width: 450px;
18 position: absolute;
19 transform: translate(-50%, -50%);
20 top: 50%;
21 left: 50%;
22 padding: 30px 40px;
23}
24
25#new-todo{
26background-color: #fff;
27padding: 30px 20px;
28border-radius: 5px;
29box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
30}
31
32#new-todo input{
33 width: 70%;
34 height: 45px;
35 font-family: 'Poppins', Verdana, Geneva, Tahoma, sans-serif;
36 font-size: 15px;
37 border: 2px solid #d1d3d4;
38 padding: 12px;
39 color: #100f0f;
40 position: relative;
41 font-weight: 500;
42 border-radius: 5px;
43}
44
45#new-todo input:focus {
46 outline: none;
47 border-color: #8052ec;
48}
49
50#new-todo button {
51 position: relative;
52 float: right;
53 width: 20%;
54 height: 45px;
55 border-radius: 5px;
56 font-family: 'Poppins', Verdana, Geneva, Tahoma, sans-serif;
57 font-weight: 500;
58 font-size: 16px;
59 border: none;
60 background-color: #8052ec;
61 color: #fff;
62 cursor: pointer;
63}
64
65#todos {
66 background-color: #fff;
67 padding: 30px 20px;
68 margin-top: 10px;
69 border-radius: 10px;
70 width: 100%;
71 box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
72 position: relative;
73}
74
75.todo {
76 background-color: #fff;
77 height: 50px;
78 padding: 5px 10px;
79 margin-top: 10px;
80 display: flex;
81 align-items: center;
82 justify-content: space-between;
83 border-bottom: 2px solid #d1d3d4;
84 cursor: pointer;
85}
86
87.todo span{
88 font-family: 'Poppins', Verdana, Geneva, Tahoma, sans-serif;
89 font-size: 15px;
90 font-weight: 400;
91}
92.todo .actions {
93  height: 100%;
94}
95.todo button {
96 background-color: #8052ec;
97 color: #fff;
98 height: 100%;
99 width: 40px;
100 border-radius: 5px;
101 border: none;
102 cursor: pointer;
103 outline: none;
104}
105
106.completed{
107 text-decoration: line-through;
108}
109

Your folder structure should look like this;

go-assets-folder-struct.jpg

You can check the code on GitHub for reference

Parse HTML template

Now we have the HJTML and CSS files set up, let’s get Golang to display them. In your main.go file, update the renderer method in func init by adding the HTML parsing option to rnd = renderer.New() .

1func init() {
2
3	rnd = renderer.New(
4		renderer.Options{
5			ParseGlobPattern: "html/*.html",
6		},
7	)
8...
9}
10

Here, we create a new renderer instance and set the ParseGlobPattern option. This option allows us to look for files inside the HTML folder with the “.html” extension and render them as templates.

Now, update the homeHandler in main.go file to return the content from the indexPage in the HTML template so that when a user makes a browser HTTP request to “localhost:9000/”, it returns the indexPage in the HTML template.

1func homeHandler(rw http.ResponseWriter, r *http.Request) {
2	// filePath := "./README.md"
3	// err := rnd.FileView(rw, http.StatusOK, filePath, "readme.md")
4
5	err := rnd.HTML(rw, http.StatusOK, "indexPage", nil)
6}
7

Here, I commented out the README.md file that was being returned and replaced it with the "HTML" template renderer, as mentioned above.

Run go run main.go to view the result.

Note: If you used my Go Todo Backend API tutorial, make sure to have your database up and running, otherwise the app will crash.

Serve Static CSS and JavaScript Files in Go.

After running main.go and navigating to localhost:9000. You notice that the HTML lacks CSS, and the URL returns a 404 error for the static files.

html-templates-static-assets/static-asseets-404.jpg

To fix this error, we must serve the static files to Go by publishing the CSS files.

To publish the static files, update func main in main.go and add the following code after where you defined middleware.Logger and before where you specified the homehandler to ensure that the static files are available when a user sends a request to localhost:9000.

/main.go

1// Serve static files
2fs := http.FileServer(http.Dir("./static"))
3router.Handle("/static/*", http.StripPrefix("/static/", fs))
4

In the code above, we use the file server handler http.fileServer to serve static files from the ‘static’ directory on the server.

The router.Handle method handles the route for serving static files. In this example, we use chi.Router,so update accordingly to whatever router you are using. This setup specifies that any URL path request starting with “/static/” should be handled by the ‘fs’ (file server ).

The http.StripPrefix removes the ‘static’ prefix from the request URL, so instead of the request URL to be domain-name/static/static/style.css, it will be domain-name/static/styles.css.

Once you complete the setup, run go run main.go and navigate to localhost:9000 in your web browser. You will see that the page has applied the styles, and the URL will return a 200 status ok for the CSS and script file( if you added one ), indicating that the server has successfully served the static files.

html-templates-static-assets/static-asset-200.jpg

JavaScript File

Adding a JavaScript file is no different from adding a CSS file. The process is exactly the same. Simply create a script.js file inside the static folder (or whichever folder name you used). After restarting the server, the JavaScript file will be picked up and served, just like the CSS file.css file.

Final Result

This is what the UI looks like with the styles applied;

html-template-css-asset-result.jpg

TypeScript Configuration and Setup

If you're using vanilla JS, you can write the JavaScript code directly in the script tag or a separate file.

But for TypeScript, It’s best to create a separate script.ts file, compile it to JavaScript using the TypeScript compiler, and then import the resulting script.js file into your HTML template.

Why? Because it was difficult to find compilers that could compile the Typescript in the <script> tag in the HTML file, and I couldn't find one that worked well enough.

To add a TypeScript file, run this npm command to install Typescript globally.

1npm install -g typescript
2

Then, check the installed version by running tsc --version.

Create a tsconfig.json file at your project's root and paste the configuration code into it. Adding the tsconfig.json file is recommended for more precise code management and better code quality.

/tsconfig.json

1{
2  "compilerOptions": {
3    "target": "es2017",
4    "module": "ESNext",
5    "sourceMap": true,
6    "strict": true,
7    "alwaysStrict": true,
8    "noImplicitAny": true,
9    "strictNullChecks": true,
10    "allowUnreachableCode": false,
11    "strictFunctionTypes": true,
12    "noUnusedParameters": true,
13    "noImplicitReturns": true
14  }
15}
16

Next, add a script.ts file to the static folder.

To compile the script.ts file,

1tsc static/script.ts
2

By default, TypeScript will output the compiled JavaScript file script.js in the same folder as the TypeScript file. If you want to change this behaviour, you can add the "outDir" option to the configuration. For example, setting "outDir": "dist" will output the compiled file to the dist folder.

Don't forget to include script.js as the script tag above </body> tag source in your HTML code.

1<script  src="/static/script.js"></script

Your folder structure should look like this;

asset-file-tsconfig.jpg

To test, add a simple code to the script.ts file (e.g. console.log(’hello’) )and compile the ts file to test. If using a middleware logger, you should see the GET request result on the terminal.

asset-ts-file-200.jpg

Now, you're all set to write and compile TypeScript code.

Get the complete code for HTML template setup and asset files on GitHub.

For TypeScript configuration, check out the file on GitHub.

Conclusion

In this continuation article, we learned how to generate HTML templates and serve static CSS and JavaScript files using Golang; we set up a Typescript configuration for people who would prefer to use it. Following the steps outlined in this guide, we can now render HTML templates, parse them, and serve static files.

With this basic setup, we are ready to build the Todo App Frontend with HTML, CSS, and Vanilla JavaScript or TypeScript, using the JavaScript Fetch API to consume the Todo backend API endpoints.

Recommended Reads

Articles written by Kelechi Ogbonna. All rights reserved

Built with NextJs Typescript and TailwindCSS