Custom content types in Hugo
One of the things that attracted me to Hugo was it's strong custom content type support. It took me a little while to understand that types in Hugo are based on the file structure. If you wanted to create a new content type of dog
, you just start adding content to the content/dog
folder. Let's create a dog named Desmond
now using Hugo:
bash
hugo new dog/Desmond.md
This will create content/dog/Desmond.md
with the following content:
yaml
---title: "Desmond"date: 2018-07-15T21:46:09+01:00draft: true---
This pre-filled content comes from an archetype
, which is the template that Hugo uses when creating a new piece of content. It doesn't make sense for a dog to have a title
, date
or be in draft
. However, it does make sense for a dog to have a name
and either be a good_dog
or not. To create a new archetype, create a file with the same name as your content type in archetypes
. In this case, create archetypes/dog.md
with the following content:
yaml
---name: "{{ .Name }}"good_dog: true---
This uses the .Name
field from Hugo (which is the name of our file, Desmond
) and says that the dog is a good_dog
(because aren't all dogs good dogs?)
Delete content/dog/Desmond.md
and then create him again using hugo new dog/Desmond.md
. If you inspect the contents of content/dog/Desmond.md
now you'll see that it follows our dog
archetype template:
yaml
---name: "Desmond"good_dog: true---
List all of our dogs
When creating a new type, you can provide a custom template that lists entries of that type by creating an list.html
file. Let's do this for our dogs by creating layouts/dog/list.html
with the following content:
html
<h3>Dogs</h3>Dogs list to be added soon
When Hugo runs, it searches for a matching template file for the current category following the Hugo template lookup rules. This means that it will use layouts/<category>/list.html
if it exists, before falling back to layouts/_default/list.html
. If neither of those exist, it'll look for the same files inside the current theme.
Create layouts/dog/list.html
with the following contents:
html
{{ define "main" }}<h3>Dogs</h3><ul class="posts">{{ range .Data.Pages }}<li>{{ .Params.name }}</li>{{ end }}</ul>{{ end }}
In Hugo all of the data is scoped to the current context. This means that .Data.Pages
only contains pages in the dog
directory when we visit http://localhost:1313/dog/. When looping over the pages the metadata on the post is available in the .Params
variable, allowing us to access the name of our dog as .Params.name
.
Once you've created this file, visit http://localhost:1313/dog/ to see a list of your dogs rendered.
Listing a page on the homepage
The final thing I wanted to do was add a list of dogs to the home page. I started by copying the loop from our layout/dog/list.html
page which rendered all of the dogs, but also rendered all other pages on the site. This is because on the home page, the scope of .Data.Pages
contains all types.
html
{{ define "main" }}<h3>All our dogs</h3><ul>{{ range .Data.Pages }}<li>{{ .Params.name }}</li>{{ end }}</ul>{{ end }}
To make it render only dogs we have to add a where
condition to our template. We can ask Hugo to loop over .Data.Pages
where the Section
is equal to dog
and only output those pages.
html
{{ define "main" }}<h3>All our dogs</h3><ul>{{ range where .Data.Pages "Section" "dog" }}<li>{{ .Params.name }}</li>{{ end }}</ul>{{ end }}
After saving this and reloading the home page we see that there is a list of dogs (and only dogs) displayed.