published on 03.08.2020

In this post, I aim to explain this website. Template by template… The main purpose here is to create a document to myself for future reference. Templates are complicated and not easy to read. That’s why I’ll probably forget what I did and why I coded this way.


We are using the default taxonomy called tags. Hugo provides 2 taxonomies by default: tags and categories. You don’t need to specify them anywhere. I’ve added the configuration below to config.yaml file to use only tagsand disable categories taxonomy.

  tag = "tags"


Hugo templates are stored in /layouts folder. There are 2 types of templates: list templates and single templates. List templates are for listing content while single templates are for displaying the content itself. For example, /tags, /categories, /posts use “list templates”; while /post/foo.html, /about pages use “single page templates”.

Home Page

Home page is a stand-alone page created impilicty by hugo. I’ve used layouts/home.html template to separate home page from others. If we want to add content to home page we should use /content/ file.

baseof.html & Hugo Blocks

Every other template is using baseof.html. I was using a similar layout in jekyll called default.html

Here is an example of Hugo blocks. This block indicates where “footer” block will be placed. What’s defined inside this block is the default for the block. Default lines will be used if it’s not overwritten by some other template.

    {{ block "footer" . }}
        <footer>{{ partial "footer.html" . }}</footer>
    {{ end }}

In another template, let’s say, list.html , we define “footer” block. So, all the content in the footer block be placed in that part of baseof.html. If there is no “footer” block defined in child template (list.html) then hugo will use the original lines from the baseof.html. Which is <footer>{{ partial "footer.html" . }}</footer> in this example.

In this site, footer is not variable but content is. I have content block in baseof.html and it has no lines of code inside which means if it’s not defined then it’ll be empty.

    {{ block "content" . }}{{ end }}

And here is the definition of content from list.html

{{  define "content" }}
  {{ partial "generate_button.html" (dict "href" "/series" "name" "Remove Filters")}}
  {{ partial "list_pages_episodes.html" . }}
{{ end }}

Listing Content

In my setup there are 3 ways of listing blog posts.

  • Tags (group by tags)
  • Series (group by sections)
  • Archives (group by date).

Filter All Blog Posts & Ordinary Pages

To display all pages across sections, we need to gather all the pages in the site. To do this I’ve used the following code.

{{ $pages := where $.Site.RegularPages ".Type" "!=" "ordinary" }}

Site.Pages will gather all the pages including “index pages”, “term pages” and home pages for each section. Site.RegularPages will filter out those pages still it will include pages like To gather only the blog posts, either we should add a meta-data in blog-posts or the pages we don’t want to display. Since it’s easier to tag the pages I don’t want to display, I added type: ordinary line in their front-matter.

There is one more way to filter those pages, I call ‘ordinary’. We can check if file contains date in it’s front-matter. This will work since I won’t enter a date for ordinary pages. Yet, it’s not obvious to understand. I find setting a meta-data in the front-matter more clean and intuitive instead of using magic variables.

{{ where $.Site.RegularPages "" "!=" "" }}

Also, we may need to check a single page to see if the page fits our criteria. To do that, we’ll not use RegularPages but (eq .Kind "page"). RegularPages is a shortcut for us. It checks if .Kind is equal to "page" for all pages and returns an array of pages.

In order to control if a single page is a post or not I use the following mechanism:

{{ if and (eq .Kind "page") (ne .Type "ordinary") }}

Tags View


These files overwrites the default list html /layouts/_defaults/list.html for tag section.

list.html is for creating the /tags page and term.html is for /tags/foo where foo is a tag. In Hugo terminology; each tag is called as a “term” in “tags” taxonomy.

Layouts above also using these partials:

  • generate_button
  • tag_cloud
  • get_post_infocard
  • pagination
  • list_pages

Archives View

  • Layout: /layouts/_default/archives.html
  • Page: /content/

Unlike tags, “series” and “archives” pages are custom pages and these are not taxonomies. Just single - ordinary pages. So, by default these pages use /_default/single.html However, I’ve added layout information in the front-matter.

title: Archives
layout: archive
type: ordinary

Archives view, is simply displays all blog posts in the website ordered by date and grouped by year-month.

Series View

  • Layout: /layouts/_default/series.html
  • Page: /content/

Similarly with the archives view, series view has layout meta-data in it’s front-matter.

title: Archives
layout: series
type: ordinary

Hugo calls, each sub-folder in the /content directory as sections. In this site, all blog posts will be there and each blog-post series will have their sub-directory. For example, I’ll create three blog-post series called “operations-research”, “jekyll” and “hugo”. Each blog-post of a series, as hugo called them sections, will have meta-data called “episode”.

Basically, series view do: group posts by sections then order by episode. Also, check “section.html” which can be considered as a term if we take series (sections) as a taxonomy.

Other Templates

I’ve two more templates, called list.html and section.html.


/layouts/_default/list.html In my design, this template is not used by any of the pages. Because, tags are overwritten by their own files and I’ve created a separate section.html for section pages.


As I said before, section.html can be considered as a term for series view. This template is used by section pages (home pages for sections) which are "/hugo", "/jekyll", "/operations-research" or "/foo". If I delete this template, section pages will start using /layouts/_default/list.html.

What if I don’t want to use any template?

Well, you don’t have to. When we use templates, we define parts inside blocks. If you don’t use any block, then the template won’t be related with baseof.html. You can create a separate <html> file, 404 page is a good example.

Blog Posts vs Series vs Projects

In my site, series are blog posts with episode parameter in their front-matter. I’ve seen many blogs using the name “tutorials” where I use the name “series” instead. I am not sure if they created different taxonomy but I didn’t. Also there are projects which is again just another section.

I considered to create different taxonomies called “series” and “projects” to separate those from others. Then I realised I had to add category taxonomy too and not use sections. As far as I understand, most people alredy don’t use sections. Taxonomies can even make somethings easier, like templating and filtering since Hugo provides built-in functions. However, I found using sections is easier, it requires less templates and folders. I don’t need to make things complicated since all these 3 are basically the same thing. Also, I don’t want to make front-matter longer.

I’ve customized projects section’s list template and single template by creating a separate folder for them /layouts/projects. So, Hugo will use these templates instead of /layouts/_default for projects section. Template Lookup Order is important here.

You are reading the 3rd of 19 episodes in Hugo.
Published on 03.08.2020 by Mert Bakır with commit f142d2f.
#hugo #static-site #workflow
Next episode:
Overwrite Default Landing Page For Hugo Taxonomies
published on 21.08.2020

“Overwriting default landing page for Hugo taxonomies”. What do I mean by that? By default, Hugo will create a home page or a landing page for your taxonomies. /tags/ or /categories are good examples. If you don’t have any specific templates for tags /layouts/tags/list.html Hugo …

published on 05.08.2020

This post will be very short and precise. In Jekyll, we had to create an .xml file using liquid templates to tell Jekyll that we want an rss.xml or similarly sitemap.xml. Luckily, Hugo creates these files for us under the hood. You can check them by going to /index.xml and /sitemap.xml pages in your …

published on 06.08.2020

Search engine bots or crawlers, use two files to crawl a website more intelligently. These are robots.txt and sitemap.xml. A robots.txt file tells search engine crawlers which pages or files the crawler can or can’t request from the site. This is used mainly to avoid overloading your site with …

published on 10.07.2022

Previously, I’ve published a blog post about deploying static content on heroku with basic authentication. The main purpose was to get basic auth for a freely hosted static website. In that post, we hosted the source code on GitLab and configured a CI/CD pipeline to render the static content …

published on 28.05.2022

Each git commit has a field called Author which consists ‘’ and ‘’. We usually set these variables once, after installing git, with git config --global so that each repo gets the variables from the global definition. We can also set them locally for a …

published on 25.05.2022

In this post, I’ll first walk through hosting static content with basic authentication. Then, we’ll look into deploying to Heroku using GitLab Pipelines, more specifically deploying a certain sub-directory within the project instead of pushing the whole project. Also, I’ll share …

published on 17.04.2022
edited on 15.07.2022

Önceki bölümde, markdown formatını LaTeX formatına dönüştürmek için kullanılan Pandoc yazılımından bahsetmiştik. Şimdi konuyu bir adım daha ileri taşıyıp ve bookdown’a geçiyoruz. Bookdown; Rmarkdown kullanarak teknik dökümanlar, kitaplar yazabilmemizi sağlayan, Yihui Xie tarafından yazılmış …

published on 10.04.2022

I’ve been using WSL-2 on Windows for over a year. It’s very useful because some Python packages are just a headache to install on Windows. Also, docker. It’s just better on Linux. Yet, WSL-2 can also be problematic. I remember trying a dual-boot setup when things just went way too …

published on 03.03.2022

In this post, I’ll share how to install geopandas and some other gis related packages on Windows. If you are on Mac or Linux you can probably just pip install those without any issue. I usually had to do a google search every time I wanted to install these packages on Windows environment. Of …