Handling taxonomies like a boss - update

Live view: http://hugo-taxonomies.netlify.com/
Repo: https://github.com/guayom/hugo-taxonomies

Here you’ll find a very detailed explanation on how to use taxonomies in a HUGO site. No hackish code or intricate tricks. There is documentation on this topic, but you’ll have to search in 3 different places and some examples are missing. So it can be very confusing at times, at least it was for me.

After reading this, you will be able to:

  • Create your own taxonomies
  • Add custom metadata to your taxonomies
  • Create a list template for taxonomy terms using all the custom metadata you added to the taxonomy
  • Handle terms without a corresponding _index.md file and define default values in your templates
  • Create a template for terms with all the data related to it
  • Speed up the creation of terms pages using archetypes

Create your own taxonomies

in config.toml

[taxonomies]
  brand = "brands"

And then list them on your pages. This is how a product page would look like:

---
title: "Adidas Ligra 5"
description: "This is the description for Adidas Ligra 5"
brands:
  - Adidas
date: 2017-08-05T07:43:13-06:00
draft: false
---

Add custom metadata to your taxonomies

Let’s say you want to add a logo, website link and description for each brand. You need to place that information in your taxonomy terms pages. For example: /content/brands/adidas/_index.md

Attention Note that the structure is important. <taxonomy>/<term>/_index.md

---
title: "Adidas"
description: "This is the description for Adidas"
website: "http://adidas.com"
logo: "/images/adidas.png"
---

Create an index template for taxonomy terms using all the custom metadata you added to the taxonomy (terms.html)

Now to create an index for your taxonomies, and display all the custom metadata you added for each term, you need to edit your terms template. You can add it here: /layouts/_default/terms.html

Range dynamically through all the term pages with .Data.Pages

<ul>
  {{ range .Data.Pages }}
    <li>
      <a href="{{ .Permalink }}">{{ .Title }}</a>
      <dl>
        <dt>Link to the brand page</dt><dd><a href="{{ .Permalink }}">{{ .Permalink }}</a></dd>

        <!-- Handling brands that don't have a corresponding page by using 'with' -->
        {{ with .Params.website }}
          <dt>Website</dt><dd>{{ . }}</dd>
        {{ end }}
        {{ with .Params.description }}
          <dt>Description</dt><dd>{{ . }}</dd>
        {{ end }}

        <!--
        Handling brands that don't have a corresponding page by using an 'if' statement.
        This let's you add default values to your template.
        -->
        <dt>Logo</dt>
        <dd>
        {{ if ne .Params.logo nil }}
          <img src="{{ .Params.logo }}" height="90" />
        {{ else }}
          <img src="http://via.placeholder.com/90x90?text=No+logo" height="90" />
        {{ end }}
        </dd>
      </dl>
    </li>
  {{ end }}
</ul>

Handle terms without a corresponding _index.md file and define default values in your templates

You might create some terms for which you haven’t created a corresponding page (/content/brands/terms/_index.md).

As an example, I added a product called New Balance Shoe. It’s brand is “New Balance”. But if you search in my content/brands folder, you won’t find a /new-balance/_index.md file. I haven’t created it.

In that case you will have to add some default parameters here. Take a look at the comments in the snippets above to find a couple ways to handle terms without a corresponding page.

Create a template for terms with all the data related to it

Now, you can edit the template for each term page. For example, the page for the brand “Nike” would be: /brands/nike. Edit this template at: /layouts/_default/taxonomy.html

You can list all the pages that are labeled with the current term via .Data.Pages, as you can see on the example below. Just as we did before, we can handle the terms that don’t have a corresponding page by using with or if statements.

{{ with .Params.description }}
  <p>{{.}}</p>
{{ end }}

<ul>
  {{ range .Data.Pages }}
     <li>
       <a href="{{ .Permalink}}">{{ .Title }}</a>
     </li>
  {{ end }}
</ul>

Speed up the creation of terms pages by using archetypes

Having to create a <taxonomy>/<term>/_index.md file for each term, can be a real hassle. But you can save a lot of time by taking advantage of Archetypes. This is a HUGE TIME SAVER. With the setup I’ll show you, whenever you want to create a new taxonomy term (brand), all you have to do is type hugo new <taxonomy>/<term>/_index.md on your terminal and your brand page will be created and even filled with the correct title.

Just create a new archetype for your taxonomy. archetypes/<taxonomy>.md. In my case, archetypes/brands.md

---
title: {{ $term := index ( split .File.Dir "/") 1 }}"{{ replace $term "-" " " | title }}"
description: "This is the description for {{ $term | title }}"
website: "http://{{ $term }}.com"
logo: "/images/{{  $term }}.jpg"
date: {{ .Date }}
draft: false
---

Note that I defined the variable $term in the first line. It takes the variable .File.Dir, splits it into an array, indexes it and grabs the second value. This value will serve as your title, and you can use it to dynamically create default values based on your file name. That way, when you create pages by the CLI, all you have to do is name them correctly, and all your data will be filled with the correct title.

Try it out:
On your terminal type:
hugo new brands/test/_index.md

It will use whatever value you use in the place of test as your title. This is what it would render:

---
title: "Test"
description: "This is the description for Test"
website: "http://test.com"
logo: "/images/test.jpg"
date: 2017-08-07T12:04:03-06:00
draft: false
---

Important: If your term contains special characters, for example “Ügly brand”, when you type the command on your terminal, you have to remove the special characters and replace spaces with “-” (ugly-brand). Then on your _index.md the spaces will be taken care of, but you will have to add any special character you replaced.

Disclaimer
This post was different originally. I edited it and added a lot of improvements. My previous code needed some tricks that aren’t necessary anymore. This code is much cleaner. You may find the previous code in the history, but I would recommend using the updated one.

14 Likes

I liked your other solution more because it dealt with superfluous taxonomy name folder. (but that topic has already been exhausted)

This solution is pretty fine also. And I cannot see how future updates will break this.

And with that no more comments from me in this forum.

Bye.

I edited the OP to add a lot of improvements.
No more hackish code. No need to use weird variables to list pages.
And I also dealt with the hassle of creating term pages in /<taxonomy>/<term>/_index.md by using an archetype.
I’m very pleased with the result. I’m developing a real project that has a very heavy usage of taxonomies, and this setup is finally what I needed.
Check it out, and let me know what you think.

2 Likes

This is a very nice example, using the “brands” taxonomy, so others can reference and see each part.

Additionally, this is how it has worked for a while, and I was able to derive it from the docs and building a site. So I guess we ought to figure out the parts you included here that weren’t in the docs you were looking at and make sure they are added.

1 Like

I agree. I’m already working on it. I want to add some examples to the taxonomy templates documentation. Specifically in the taxonomy templates section. I’d like to add an example for the taxnomy.html and terms templates.html. I’m still reading the guidelines to do that.

Also, the archetype for taxonomies is a little different from a normal page. So I think that would be useful too.

As I said in the intro, you can derive everything from the docs. That’s how I did it. But it took me about a week to get everything to work as I needed to and put the pieces together. So I compiled everything in one place, and hopefully help someone save some time.

2 Likes

I am not sure if this example ought to be in the archetypes docs, but something similar definitely. We want the docs to stay accessible enough for new folks, but doing something interesting like what you’ve done can show their flexibility.

I was checking to see if there is a tutorials section, but maybe we ought to just link to this thread from one or more of those pages.

You’re right. I’ve thinking about it and I’d do 2 things in the archetypes documentation:

  • indicate that in your custom archetype template you can use the file variables.
  • Where it explains how the filename is generated, explain how you can use those variables to dynamically populate your custom archetypes template.
1 Like

I flagged this message, maybe a mod will spin it off into a new topic. Please start new topics if your post doesn’t follow the original post or title.

Archetypes are just a pre-defined template for content. The content itself goes into sections, and that is where it uses the template lookup.

The example given in the original post is advanced, so you shouldn’t think about it until you get the basics of archetypes. :slight_smile:

1 Like

@guayom Thank you so much for the detailed write-up! Superb work.

One question, how could I have a .active link class for the Adidas page for example? So when you click on Brands, the Brands link is active and then when you click on Adidas the Brands link is still active?

I use the standard Hugo menu code but can’t get my head around keeping the taxonomy link active when on a parent page:

{{ $currentPage := . }}
{{ range .Site.Menus.main }}
  <a class="{{if or ($currentPage.IsMenuCurrent "main" .) 
($currentPage.HasMenuCurrent "main" .) }}active{{end}}" href=" . 
{{.URL}}">{{ .Name }}</a>
{{ end }}