Template consists of layouts. There are three types of layouts:
- Layout for home page.
- Layout for single pages.
- Layout for list pages.
Layout for home page
File layouts/index.html. Example:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>{{ .Site.Title }}</title>
</head>
<body>
<h1>{{ .Site.Title }}</h1>
{{ .Content }}
</body>
</html>
Content file: content/_index.md.
You can place any valid Markdown content into this file.
Layout for single pages
You can create different single page layouts for each content type. These get stored in
subdirectories of the layouts directory.
To create a default single page layout that every content page will use, store it in the
layouts/_default directory.
Layout file: layouts/_default/single.html.
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>{{ .Site.Title }}</title>
</head>
<body>
<h1>{{ .Site.Title }}</h1>
<h2>{{ .Title }}</h2>
{{ .Content }}
</body>
</html>
Layout for lists
Layout file: layouts/_default/list.html.
{{ define "main" }}
<h2>{{ .Title }}</h2>
{{ .Content }}
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
The {{ range .Pages }} section is where the magic hapens. The .Pages collection contains all of the pages related to
the section you’re working with. When Hugo builds the list page for Projects, for example, it collects all the pages
withing content/projects and makes those available in the .Pages variable in the context. The range function
lets you iterate over the results so you can display each record in the collection. Inside of the range block, you
access the properties of each page, as the context is now set to that specific page’s context. In this case,
you’re displaying the site-relative URL and title of each page.
For taxonomies
themes/basic/layouts/_default/year.html
themes/basic/layouts/_default/month.html
Layout for pages from a particular section
The layout file will be: layouts/projects/single.html.
{{ define "main" }}
<div class="project-container">
<section class="project-list">
<h2>Projects</h2>
<ul>
{{ range (where .Site.RegularPages "Type" "in" "projects") }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
</section>
<section class="project">
<h2>{{ .Title }}</h2>
{{ .Content }}
</section>
</div>
{{ end }}
To add some content to the page, we need to add _index.md file to the folder associated with the content.
hugo new projects/_index.md
Since you’re not working with a collection in a list layout, you won’t have access to a .Pages variable. Hugo provides a mechanism
where you can access any content collection. The .Site.RegularPages variable gives you access to all of the pages
in your site. You can whittle that down with the where function, which works similar to how a SQL statement
works. For example, to find all of the Project pages, you’d write this statement:
{{ range (where .Site.RegularPages "Type" "in" "projects") }}
You can sort the items too. If you’d like to order them by the most recent project first, use the .ByDate function
and the .Reverse function li this:
{{ range (where .Site.RegularPages "Type" "in" "projects").ByDate.Reverse }}
Context (scope)
When you’re working with a Hugo layout, there’s a “scope”, or “context” that contains
the data you want to access. The current context in a template is represented by the dot.
In a Hugo layout, the context is set to the Page context, which looks something like this:
Context (.)
|-- Site
|-- Title
|-- Title
|-- Content
For convenience, Hugo makes all of the site’s data available in the Page context under the
Site key. That’s why you can do .Site.Title to get the site’s title. To get the page title,
you’d use .Title.
The .Site prefix tells Hugo to get the value from the site’s data rather than from the
page’s data.
The {{ .Site.Title }} lines pull the title field out of the config.toml file.
The {{ .Content }} line displays the content for the page, which will come from an associated
Markdown document in your site’s content folder.
{{ .RelPermalink }} - link to the page.
Page params
We can use data from the front matter.
archetypes/projects.md:
---
title: "{{ replace .Name "-" " " | title }}"
draft: false
image: //via.placeholder.com/640x150
alt_text: "{{ replace .Name "-" " " | title }} screenshot"
summary: "Summary of the {{ replace .Name "-" " " | title }} project"
tech_used:
- JavaScript
- CSS
- HTML
---
Description of the {{ replace .Name "-" " " | title }} project...
### Tech used
* item
* item
* item
In the layout:
<img alt="{{ .Params.alt_text }}" src="{{ .Params.image }}">
<h3>Tech used</h3>
<ul>
{{ range .Params.tech_used }}
<li>{{ . }}</li>
{{ end }}
</ul>
Notice that the image, alt_text, and tech_used fields are prefixed by .Params. Any custom fields you add to the
front matter get added to this .Params collection.
Predefined fields: open_in_new https://gohugo.io/content-management/front-matter/#predefined
Also, we can use these params in other templates. For example, in list.html:
{{ range .Pages }}
<section class="project">
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
<p>{{ .Summary }}</p>
</section>
{{ end }}
Hugo can automatically generate a page’s summary, but the summary field in the front matter overrides that, giving you more
control.
Include CSS
<link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
The relURL function creates site-root-relative link to the file instead of an absolute link that contains the site’s
domain name. For ease of development and deployment, you should use relative URLs whenever possible. When
Hugo generates the site, it’ll copy the content of the themes/basic/static directory, including all
subdirectories, into the public directory, so your CSS file will be located at /css/style.css.
You kept the stylesheet in the static directory of the theme instead of in the static directory in your Hugo site.
It’s best to follow this convention and use your site’s static directory for images of other assets that are specific to your site,
and keep things that are specific to the theme within the theme’s directory structure. When you generate your site,
Hugo will grab files from both places and put them in the public directory. If you name files the same,
the ones in your site’s static directory will override the ones in the theme.
Custom layout for some page
layouts/_default/contact.html (in a theme or in a Hugo’s layouts).
Then in some page we add the next line to the front matter:
layout: contact
Conditionally displaying data
Hugo has an isset function which, at first glance, looks like a great way to check whether a variable has a value.
Unfortunately, it has limitations that aren’t intuitive. It doesn’t handle situations where values are always
defined but are empty, like default values. The Description field on a page is actually a default field. If you don’t
define it in your page, its value will be defined, but empty, and isset won’t work.
To handle cases where you’re checking for a value in a default variable like this, use Hugo’s with function.
{{- with .Page.Description -}}
{{ . }}
{{- else -}}
{{ .Site.Params.description }}
{{- end -}}
The with function rebinds the context. Using with, you can switch the current context to .Page.Description. Then
withing that block, a single period prints out the value. But the with function has a nice side effect: if the value
of the context is empty, then the block gets skipped and nothing gets rendered. To handle the case where there’s no
description, you pair the with function with an else statement.
Andrew Dorokhov