John Siu Blog

Tech - Business Tool, Personal Toys

Hugo Theme SK1 Walk Through

☰ Table of Content

A starting guide for creating Hugo theme.

This guide assume some familiarity on creating a Hugo site using other Hugo themes.

Base Directory

In an empty directory:

1
hugo new theme newtheme

This will create a new theme directory themes/newtheme:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.
├── resources
│   └── _gen
│       ├── assets
│       └── images
└── themes
    └── mytheme
        ├── LICENSE
        ├── archetypes/
        │   └── default.md
        ├── layouts/
        │   ├── 404.html
        │   ├── _default
        │   │   ├── baseof.html
        │   │   ├── list.html
        │   │   └── single.html
        │   ├── index.html
        │   └── partials/
        │       ├── footer.html
        │       ├── head.html
        │       └── header.html
        ├── static/
        │   ├── css/
        │   └── js/
        └── theme.toml

Only mytheme directory is needed. resources directory can be deleted.

MyGit

Shameless self promotion! 🤣

If you have mygit setup, use following to create repository on github/gogs/gitea:

1
2
3
4
5
6
7
cd themes/mytheme
mygit init
mygit repo new
mygit repo description "Hugo Theme - MyTheme"
git add .
git commit -a -m "Initial commit."
mygit push --master

Else do following for local git:

1
2
3
4
cd themes/mytheme
git init
git add .
git commit -a -m "Initial commit."

Sample Content

Once git is initialized, pull in Hugo example site content as sub-module:

1
2
# Inside mytheme/
git submodule add https://github.com/gohugoio/hugoBasicExample.git exampleSite

Test:

1
2
3
# Inside mytheme/
cd exampleSite
hugo server --theme mytheme --themesDir ../../

On screen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
                   | EN
-------------------+-----
  Pages            | 19
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  1
  Processed images |  0
  Aliases          |  9
  Sitemaps         |  1
  Cleaned          |  0

Built in 12 ms
Watching for changes in /Users/js/tmp/themes/mytheme/{archetypes,exampleSite,layouts,static}
Watching for config changes in /Users/js/tmp/themes/mytheme/exampleSite/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

Open browser to http://localhost:1313, it should show an empty page. That is correct result for now. Keep it running and you will see changes made to theme in real time.

_default

mytheme/layouts/_default/ contains 3 files: baseof, list and single. Lets go through them one by one.

baseof.html

_default/baseof.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!DOCTYPE html>
<html>
{{- partial "head.html" . -}}

<body>
	{{- partial "header.html" . -}}
	<div id="content">
		{{- block "main" . }}{{- end }}
	</div>
	{{- partial "footer.html" . -}}
</body>

</html>

_default/baseof.html1 is the base, outer most wrapper of ALL pages generated. No change required for this project.

It include 3 partials: head.html, header.html and footer.html, which will be looked at in partials section below.

list.html

_default/list.html2 is template for all listing pages. The reason http://localhost:1313 is blank is because out list.html is empty.

Base on Hugo documentation, following is a simple list.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{{define "main"}}

<!--$paginator-->
{{if .IsHome}}
{{.Scratch.Set "Paginator" (.Paginate (where .Site.RegularPages "Type" "in" site.Params.mainSections))}}
{{else}}
{{.Scratch.Set "Paginator" .Paginator}}
{{end}}
{{$paginator:=(.Scratch.Get "Paginator")}}

<!--list-->
<ul>
	{{range $paginator.Pages}}
	<li>{{.Date.Format "2006-01-02"}} | <a href="{{.RelPermalink}}">{{.Title}}</a></li>
	{{end}}
</ul>

<!--pagination-->
{{template "_internal/pagination.html" .}}

{{end}}

Save and refresh browser. However browser will remain blank at this point. That is because layouts/index.html is empty.

index.html is used as home page when available, else list.html is used.

For simplicity of this project, just delete index.html and refresh browser.

More information about pagination3 is in Hugo doc.

single.html

_default/single.html4 is template for all content pages. The generated file is empty. Clicking on any page link in listing page will show page not found, as no content page is generated.

Supply single.html with following content and save.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{{define "main"}}

<!--Title-->
<h2>{{.Title}}</h2>

<!--Date-->
{{.Date.Format "2006-01-30"}}

<!--TOC-->
{{.TableOfContents}}

<!--Content-->
{{.Content}}

<!--Tag-->
<ul>
	{{range (.GetTerms "tags")}}
	<li><a href="{{.RelPermalink}}">{{.LinkTitle}}</a></li>
	{{end}}
</ul>

<!--Prev/Next-->
{{with .PrevInSection}}Prev <a href="{{.RelPermalink}}">{{.Title}}</a>{{end}}
{{with .NextInSection}}Next <a href="{{.RelPermalink}}">{{.Title}}</a>{{end}}

<!--Related-->
{{$related := .Site.RegularPages.Related . | first 5}}
{{with $related}}
<h3>See Also</h3>
<ul>
	{{range .}}
	<li><a href="{{.RelPermalink}}">{{.Title}}</a></li>
	{{end}}
</ul>
{{end}}

{{end}}

Content link should be working now.

partials

partials are common section included by other default templates or other partials.

head.html

partials/head.html, as seen in _default/baseof.html, is used outside of <body> section.

head.html should contain <head> section of html page, which usually consist of meta tag, javascript, etc.

As this project focus on making a simple Hugo template, the file is left empty.

header.html

partials/header.html5, contrary to head.html, contain display element. It is part of html <body> and usually consist of page title, menu, etc.

Following is header of SK1:

1
2
3
4
<!--Site Title-->
<a href="{{ "" | relLangURL}}">{{site.Title}}</a>
<!--Sub-title-->
{{with site.Params.subtitle}} / {{.}}{{end}}

footer.html

partials/footer.html6, similar to header.html, contain display element, but for the bottom of all pages. It is part of html <body> and usually consist of copyright, menu, etc.

Following is footer of SK1:

1
2
3
{{with .Site.Copyright}}{{.}} / {{end}}
Power by <a href="https://gohugo.io">Hugo</a> /
Theme <a href="https://github.com/J-Siu/hugo-theme-sk1/">SK1</a> by <a href="https://github.com/J-Siu/">J-Siu</a>

Additional Reference

The above give a basic idea of how a Hugo theme is put together. Following are more starting points for Hugo theme development:

The search box in Hugo site is your friend!!

For basic theming with CSS, check out hugo-theme-sk2 on GitHub.

SK Themes

ThemeGitHubHugoDemoDescription
SK1hugo-theme-sk1SK1sk1.jsiu.devFully functional basic Hugo theme with no css, no javascript.
SK2hugo-theme-sk2SK2sk2.jsiu.devFully functional basic Hugo theme with minimum css.
SK3hugo-theme-sk3SK3sk3.jsiu.devFull feature Hugo theme with Google AdSense support.

  1. Define the Base Template ↩︎

  2. Hugo List Defaults ↩︎

  3. Hugo Pagination ↩︎

  4. Hugo Single Page Templates ↩︎

  5. Hugo Partials - Header ↩︎

  6. Hugo Partials - Footer ↩︎

John Siu

Update: 2020-08-28
comments powered by Disqus