Hugo for Beginners A complete beginners’ guide on developing a blog site with Hugo static site generator Author: M. Hasan License: Creative Commons license CC BY-SA 4.0 DOI: doi.org/10.5281/zenodo.3887269 Online Version: pilinux.me/hugo/hugo-for-beginners Code Examples: github.com/piLinuxME/collections/hugo/hugo-for-beginners Abstract Written in one of the fastest-growing languagesi Golang, Hugo excels in building static sites from local markdown files and dynamic API-driven ii content at a blistering speediii. There are numerous free themesiv available online for rapid prototyping and development of a Hugo based blog site. This tutorial concentrates on developing a Hugo based site from scratch without importing any already developed theme. Contents Abstract ................................................................................................................................................... 1 Introduction ............................................................................................................................................ 2 Environment Setup ................................................................................................................................. 2 Windows 10 (64-bit) OS ...................................................................................................................... 2 Ubuntu 18.04.4 LTS (64-bit) OS........................................................................................................... 3 New Site Creation ................................................................................................................................... 4 Theme Development .............................................................................................................................. 4 Template for Regular Pages ................................................................................................................ 7 Template for Listing Pages ................................................................................................................ 15 Template for Homepage ................................................................................................................... 16 Template for 404 Error Page ............................................................................................................. 17 Summary ............................................................................................................................................... 18 References ............................................................................................................................................ 18 Introduction Unlike dynamic websites, static sites have fewer or no dependencies on databases, application servers and thus provides enhanced security, faster loading speed, and improved performance for end-users. Manually maintaining and updating each page of a static site is cumbersome. Hence, static site generators were born where the developer writes the functionality of the site, the content editor publishes or updates articles, and finally, the website generator renders the contents into HTML files and saves thousands of development and maintenance hours. Out of the 283 opensource static site generators enlisted on staticgen.comv, here website development using Hugo generator is discussed thoroughly. Environment Setup For different architectures and operating systems (OS), the compiled versions of Hugo along with the source codes are available for free on Githubvi under version 2.0 of the Apache Licensevii. Hugo version 0.72.0, Windows 10 (64-bit), and Ubuntu 18.04.4 LTS (64-bit) are considered for this tutorial. Windows 10 (64-bit) OS Hugo binary file for Windows OS can be extracted from github.com/gohugoio/hugo/releases/download/v0.72.0/hugo_0.72.0_Windows-64bit.zip and can be executed from the command-line interpreter. For demonstration, here hugo.exe file is saved in D:\Hugo_workspace directory. On a Windows machine, hugo.exe must be located in the same directory from where Hugo’s command-line interface (CLI) needs to be accessed unless the "PATH" environment variable is set. # change directory D:\>cd Hugo_workspace # check Hugo version D:\Hugo_workspace>hugo version # output: # Hugo Static Site Generator v0.72.0-8A7EF3CF windows/amd64 BuildDate: 2020-05-31T12:08:42Z Listing 1: Run Hugo on a Windows 10 64-bit machine 2 Ubuntu 18.04.4 LTS (64-bit) OS Hugo binaries have no dependency. After placing the binary file in /usr/local/bin directory, Hugo’s CLI can be accessed from any location. # home directory cd # fetch binary file wget https://github.com/gohugoio/hugo/releases/download/v0.72.0/hugo_0.72.0_Linux-64bit.tar.gz # extract tar -xvf hugo_0.72.0_Linux-64bit.tar.gz # change directory cd /usr/local/bin/ # copy hugo binary file sudo cp ~/hugo . # return back to the home directory cd # check Hugo version hugo version # output: # Hugo Static Site Generator v0.72.0-8A7EF3CF linux/amd64 BuildDate: 2020-05-31T12:07:45Z Listing 2: Run Hugo on a Linux 64-bit machine 3 New Site Creation With a one-line command hugo new site tutorial , Hugo creates a new website scaffolded at tutorial project directory. The newly-created site’s directory structure is: Table 1: Directory structure of a newly scaffolded Hugo site |── archetypes |── default.md |── content |── data |── layouts |── static |── themes |── config.toml Theme Development This tutorial follows more of a pragmatic approach rather than diving into theories. With the development of a Hugo theme, directory structure and different features of Hugo will be explored. At the root of the project, in this case at tutorial/ path, executing hugo new theme custom creates a new theme named "custom" at tutorial/themes/custom location. The directory structure of custom is: Table 2: Directory structure of a newly generated theme |── archetypes |── default.md |── layouts |── _default |── baseof.html |── list.html |── single.html |── partials |── footer.html |── head.html |── header.html |── 404.html |── index.html |── static 4 |── css |── js |── LICENSE |── theme.toml Hugo uses Golang’s data-driven html/templateviii package for generating HTML output safe against code injection and text/templateix package to generate textual output. custom/layouts contains skeletons for four types of pages: Homepage Listing pages (index pages etc.) Regular pages (about-us page, blog posts or articles, etc.) Custom 404 error page Listing 3 contains HTML codes of a single webpage built with the Bootstrap CSS and JS libraries. In the upcoming lessons, a Hugo theme is created from this webpage. <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta http-equiv="x-ua-compatible" content="IE=edge" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" /> <style> .px-6 { padding-top: 4.5rem !important; padding-bottom: 4.5rem !important; } a{ color: #000; } .navbar-nav-text { color: #000 !important; } h1, h2, h3, h4, h5, h6 { font-weight: 400; } </style> 5 <title>Hugo for beginners</title> </head> <body> <div class="navbar navbar-expand-lg fixed-top navbar-light bg-white"> <div class="container"> <a href="/" class="navbar-brand">Hugo for beginners</a> <button class="navbar-toggler" type="button" data-toggle="collapse" datatarget="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav"> <li class="nav-item dropdown"> <a class="nav-link navbar-nav-text" data-toggle="dropdown" href="#" id="blog"> Blog </a> <div class="dropdown-menu" aria-labelledby="web-development"> <a class="dropdown-item navbar-nav-text" href="/web-development/"> Web Development </a> </div> </li> </ul> </div> </div> </div> <div class="container px-6"> <div class="row"> <div class="col-md-12"> Hello world! </div> </div> <br /><br /><br /> </div> <footer> <div class="container"> <div class="card"> <div class="card-body py-2"> <p class="card-text text-center"> <a href="./">Hugo for beginners</a> - Content available under a Creative Commons license <a href="https://creativecommons.org/licenses/by-sa/4.0" target="_blank" rel="noreferrer">CC BY-SA 4.0</a> unless otherwise specified. 6 </p> </div> </div> <br /> </div> </footer> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> </body> </html> Listing 3: HTML template of a single webpage Template for Regular Pages In the PDF version of this article, Listing 3 contains four different blocks denoted by four different colors – light gray, sky blue, light orange, and light gold in ascending order. On the web version, lines 4 – 28 refers to 'head' block, lines 32 – 53 refers to the 'header' block, lines 55 – 64 belongs to the 'main' block and lines 66 – 85 belongs to the 'footer' section. themes/custom/layouts/_default/baseof.html is the default template for all rendered regular pages. Listing 4 shows an example of the baseof.html template. <!DOCTYPE html> <html lang="en"> {{ block "head" . }}{{ end }} <body> {{ block "header" . }}{{ end }} {{ block "main" . }}{{ end }} {{ partial "footer.html" . }} 7 </body> </html> Listing 4: Example of a baseof.html template themes/custom/layouts/_default/single.html inherits from baseof.html template. Hence, in this example, only the three blocks – "head", "header" and "main" need to be defined in single.html template. Listing 5 shows the first version of the single.html template that contains definitions of the three blocks each starting with {{ define "block-name" }} and ending with {{ end }}. {{ define "head" }} <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta http-equiv="x-ua-compatible" content="IE=edge" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" /> <style> .px-6 { padding-top: 4.5rem !important; padding-bottom: 4.5rem !important; } a{ color: #000; } .navbar-nav-text { color: #000 !important; } h1, h2, h3, h4, h5, h6 { font-weight: 400; } </style> <title>Hugo for beginners</title> </head> {{ end }} {{ define "header" }} <div class="navbar navbar-expand-lg fixed-top navbar-light bg-white"> <div class="container"> <a href="/" class="navbar-brand">Hugo for beginners</a> 8 <button class="navbar-toggler" type="button" data-toggle="collapse" datatarget="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav"> <li class="nav-item dropdown"> <a class="nav-link navbar-nav-text" data-toggle="dropdown" href="#" id="blog"> Blog </a> <div class="dropdown-menu" aria-labelledby="web-development"> <a class="dropdown-item navbar-nav-text" href="/web-development/"> Web Development </a> </div> </li> </ul> </div> </div> </div> {{ end }} {{ define "main" }} <div class="container px-6"> <div class="row"> <div class="col-md-12"> Hello world! </div> </div> <br /><br /><br /> </div> {{ end }} Listing 5: Example of a single.html template 9 Using partial templatesx single.html template can be refactored easily to keep the templating DRY (don’t repeat yourself). Listing 6 is the refactored version of the single.html template. {{ define "head" }} {{ partial "head.html" . }} {{ end }} {{ define "header" }} {{ partial "header.html" . }} {{ end }} {{ define "main" }} {{ partial "main.html" . }} {{ end }} Listing 6: single.html template after code refactoring IMPORTANT: The trailing dot in {{ partial "partial-name.html" . }} is a must to include another template in the current template! Hugo looks for the partial templates in the following paths: layouts/partials/<PARTIALNAME>.html themes/<THEME>/layouts/partials/<PARTIALNAME>.html For this tutorial, the partial templates are saved at themes/custom/layouts/partials/ location to make it easily portable. Listings 7 – 9 contain codes of the three partial templates – head.html, header.html, and main.html. <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta http-equiv="x-ua-compatible" content="IE=edge" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" /> <style> .px-6 { padding-top: 4.5rem !important; 10 padding-bottom: 4.5rem !important; } a{ color: #000; } .navbar-nav-text { color: #000 !important; } h1, h2, h3, h4, h5, h6 { font-weight: 400; } </style> <title>Hugo for beginners</title> </head> Listing 7: head.html partial template <div class="navbar navbar-expand-lg fixed-top navbar-light bg-white"> <div class="container"> <a href="/" class="navbar-brand">Hugo for beginners</a> <button class="navbar-toggler" type="button" data-toggle="collapse" datatarget="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav"> <li class="nav-item dropdown"> <a class="nav-link navbar-nav-text" data-toggle="dropdown" href="#" id="blog"> Blog </a> <div class="dropdown-menu" aria-labelledby="web-development"> <a class="dropdown-item navbar-nav-text" href="/web-development/"> Web Development </a> </div> </li> </ul> </div> </div> </div> Listing 8: header.html partial template 11 <div class="container px-6"> <div class="row"> <div class="col-md-12"> Hello world! </div> </div> <br /><br /><br /> </div> Listing 9: main.html partial template The footer.html partial template, called by baseof.html template, contains – <footer> <div class="container"> <div class="card"> <div class="card-body py-2"> <p class="card-text text-center"> <a href="./">Hugo for beginners</a> - Content available under a Creative Commons license <a href="https://creativecommons.org/licenses/by-sa/4.0" target="_blank" rel="noreferrer">CC BY-SA 4.0</a> unless otherwise specified. </p> </div> </div> <br /> </div> </footer> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> Listing 10: footer.html partial template 12 By default, Hugo searches the layouts directory (in this case, tutorial/layouts/ ) for templates. Therefore, Hugo generator needs to be informed to load the theme named custom by adding theme = "custom" into the config.toml file located at the root of the project (tutorial/ directory). Setting the base URLxi of the site, activation of the relative URLs, and updating the title of the site is done by editing the config.toml file. baseURL = "/" title = "Hugo for beginners" languageCode = "en-us" theme = "custom" relativeURLs = true Listing 11: config.toml file for this tutorial Now executing hugo new web-development/demo-article.md at the root of the project creates a new markdown file demo-article.md in tutorial/content/web-development/ directory. The content of demo-article.md is: --title: "Demo Article" date: 2020-06-07T15:59:27+02:00 draft: true --Listing 12: Contents of demo-article.md The metadata mentioned in listing 12 is generated automatically from the front matterxii defined in default.md file located at tutorial/archetypes/ path. Setting draft: false in demo-article.md and starting the server by executing hugo server renders the site in development mode and the newly created page becomes accessible at localhost:1313/web-development/demoarticle/. But still, adding some content in demo-article.md is not being rendered in the browser. Updating the main.html partial template by replacing Hello world! inside <div class="colmd-12"> … </div> with {{ .Content }}, Hugo will start rendering contents from the demo-article.md file. 13 <div class="container px-6"> <div class="row"> <div class="col-md-12"> {{ .Content }} </div> </div> <br /><br /><br /> </div> Listing 13: Version 2 of main.html partial template Hugo can fetch much site-related information dynamically during the generation of static pages by using many built-in and user-defined site-variablesxiii and page-variablesxiv. Two of the most used variables are: .Title: represents the title of a page, usually loads the value from the front matter of the markdown file of the respective page .Site.Title: represents the title of the site, value is set in the config.toml file located at the root of the project NOTE: The dot-prefix {{ . }} refers to the current context. The following two listings contain code snippets of the updated version of head.html and header.html partial templates where static contents are replaced by variables (only the modified lines are mentioned). <title>{{ .Title }}</title> Listing 14: Replacing static page-title with the page-variable in head.html partial template <a href="/" class="navbar-brand">{{ .Site.Title }}</a> Listing 15: Replacing static site-title with the site-variable in header.html partial template 14 Template for Listing Pages A list pagexv, having access to all page-variables, makes it easy to render pieces of contents from other regular pages in different orders. Hugo looks up for the list page’s template list.html in themes/<THEME>/layouts/_default/ directory, and for this tutorial in themes/custom/layouts/_default/ directory. The following listing demonstrates an example of a list template. <!DOCTYPE html> <html lang="en"> {{ block "head" . }}{{ end }} <body> {{ block "header" . }}{{ end }} {{ block "main" . }}{{ end }} {{ partial "footer.html" . }} </body> </html> {{ define "head" }} {{ partial "head.html" . }} {{ end }} {{ define "header" }} {{ partial "header.html" . }} {{ end }} {{ define "main" }} <div class="container px-6"> <div class="row"> <div class="col-md-12"> <ul style="list-style: none;"> {{ range .RegularPages }} {{ $pageUrl := replace .Permalink ( printf "%s" .Site.BaseURL) "" }} {{ $pageUrlFull := (printf "/%s" $pageUrl) }} <li><a href="{{ $pageUrlFull }}">{{ .Title }}</a></li> {{ end }} </ul> </div> </div> <br /><br /><br /> </div> {{ end }} Listing 16: list.html template 15 In this example, list pages show all available regular pages of the site. Since in 'Templates for Regular Pages' section, a regular page titled 'Demo Article' has been created in webdevelopment/ directory, automatically a listing page is generated at localhost:1313/webdevelopment/ which contains the link and title of the 'Demo Article' page. Template for Homepage Homepage is a special type of listing page. It can include all or some of the blocks from the list.html template defined by the block keyword. Hence, creating a basic template for homepage is quite easy as adding codes from listing 17 in themes/<THEME>/layouts/index.html file. <!DOCTYPE html> <html lang="en"> {{ block "head" . }}{{ end }} <body> {{ block "header" . }}{{ end }} {{ block "mainHome" . }}{{ end }} {{ partial "footer.html" . }} </body> </html> {{ define "mainHome" }} <div class="container px-6"> <div class="row"> <div class="col-md-12"> Hello from homepage! </div> </div> <br /><br /><br /> </div> {{ end }} Listing 17: index.html template 16 In this example, 'head' and 'header' blocks are included from list.html template. A new block 'mainHome' is introduced in index.html template to implement functions different than the listing pages. NOTE: If the same block name with different codes are implemented, block on list.html template gets overwritten by the block of index.html template. Template for 404 Error Page Like single-page template, 404-page template also inherits from baseof.html template. Listing 18 contains example codes of a 404-page template. {{ define "head" }} {{ partial "head.html" . }} {{ end }} {{ define "header" }} {{ partial "header.html" . }} {{ end }} {{ define "main" }} <div class="container px-6"> <div class="row"> <div class="col-md-12"> Lost in the space! </div> </div> <br /><br /><br /> </div> {{ end }} Listing 18: 404.html template 17 Summary As the title suggests, this tutorial is mostly for them interested in starting their blog sites or migrating from database-driven platforms to static sites using Hugo static site generator. Any reader should be able to build a functioning blog following the steps mentioned in this tutorial. Addressing different aspects of the Go templating language is not the focus of this article. If any reader aspires to build a sophisticated platform with Hugo, he or she should browse the official website and Github repository of Hugo until the author of this article publishes more tutorials for advanced developers. References i Github.com, “The State of the Octoverse,” The State of the Octoverse, accessed May 31, 2020, https://octoverse.github.com/#top-languages. ii “Data Templates | Hugo,” February 1, 2017, https://gohugo.io/templates/data-templates/#data-drivencontent. iii “Hugo vs Jekyll: Benchmarked,” January 26, 2018, https://forestry.io/blog/hugo-vs-jekyll-benchmark/. iv “Complete List | Hugo Themes,” accessed June 4, 2020, https://themes.gohugo.io/. v “StaticGen | Top Open Source Static Site Generators,” accessed June 4, 2020, https://www.staticgen.com/. vi “Releases - Gohugoio/Hugo,” GitHub, accessed June 4, 2020, https://github.com/gohugoio/hugo/releases. vii “Apache License, Version 2.0,” accessed June 4, 2020, https://www.apache.org/licenses/LICENSE-2.0. viii “Html/Template - The Go Programming Language,” accessed June 5, 2020, https://golang.org/pkg/html/template/. ix “Text/Template - The Go Programming Language,” accessed June 5, 2020, https://golang.org/pkg/text/template/. x “Partial Templates | Hugo,” February 1, 2017, https://gohugo.io/templates/partials/. xi “Configure Hugo | Hugo,” January 2, 2017, https://gohugo.io/getting-started/configuration/. xii “Front Matter | Hugo,” January 9, 2017, https://gohugo.io/content-management/front-matter/. xiii “Site Variables | Hugo,” February 1, 2017, https://gohugo.io/variables/site/. xiv “Page Variables | Hugo,” February 1, 2017, https://gohugo.io/variables/page/. xv “Lists of Content in Hugo | Hugo,” February 1, 2017, https://gohugo.io/templates/lists/. 18