How to add authors to your Hugo site

Do you want author bios to appear automatically at the end of each blog post? How about a page listing all authors? Here’s how to do all that, and more.

Ron Erdos • Updated September 21, 2019
Tested with Hugo version 0.58.3

What can you do with authors in Hugo?

Do you want Hugo to automatically display the author of each post?

Perhaps you’d like to put their name right under the main headline, just like on this site?

Maybe you’d like to show a photo and Twitter link for each author?

How about a page which shows all authors, that Hugo automatically keeps up to date for you? (Such a page is required if you want to get your site into Google News).

If you answered yes to any of the above, read on to find out how.

Step 1: Add each author to the front matter of their posts

Let’s say you had an author named Henry Mystery (cool name, huh?) and he’s written an article for you about the best cigar stores in Venice, Italy.

Let’s say this article lives in your repo at /content/posts/venice-cigars.md

First, you’d add Henry to the authors field in the front matter of the article:

---
title: "Best cigar stores in Venice, Italy"
date: 2019-09-03 12:00:00 +11:00
authors: ["Henry Mystery"]
---

I used YAML above, but you can use TOML or JSON too.

Note that the value of authors needs to be an array, even if you only have one author. If you’re not familiar with arrays, if you enclose your author’s name with square brackets [ ], then it’s in an array, and we’re ready to go.

As an aside, all taxonomy values—including tags and categories—need to be in array form:

tags: ["cigars", "shopping", "espionage"]

And there you have it—that’s the first step done. There won’t be any changes in the rendered HTML of our website yet, but we’ve laid the groundwork. Onward!

Step 2: Add an author byline beneath the headline (optional)

If you want a byline—in which the author’s name appears beneath the headline—here’s how to do that.

Inside /layouts/default/single.html, you could do something like this:

{{ partial "header.html" . }}
<h1>{{ .Title }}</h1>
<span>
	{{ range .Param "authors" }}
		{{ . }}
	{{ end }}
</span>

Inside the <span>, we’re ranging (iterating) over the authors array we created in our post front matter. Again, we only had one author, but that’s perfectly fine.

The . represents the current context, which means it represents each individual author in the front matter array in turn. However, since we only have one author in our example, the . only ever represents Henry Mystery.

The double curly braces {{ and }} around the dot means Hugo will output (print) its contents. So our rendered HTML in our blog post will look like this:

<h1>Best cigar stores in Venice, Italy</h1>
<span>Henry Mystery</span>

That’s it—you now have author bylines on your Hugo site!

Step 3: Create the author taxonomy (required if you want author bios and/or author pages)

In config.toml, create the author taxonomy:

[taxonomies]
	author = "authors"

If you aren’t using tags or categories, you can skip to the next step.

If you are using tags and/or categories, you’ll need to add these to your taxonomies, as well as the new author taxonomy:

[taxonomies]
	author = "authors"
	tag = "tags"
	category = "categories"

“But they were working fine before, why do I have to add them now?”, I hear you ask.

Well, in a site without any custom taxonomies, Hugo sets up the tags and categories taxonomies for you, without you having to specify them in config.toml.

However, as soon as you create custom taxonomies, you also need to manually declare the default taxonomies of tags and categories. Otherwise, Hugo thinks you don’t want them anymore.

OK, done. No changes to our HTML from this step, but we’ve laid the groundwork for the remaining two steps.

Step 4: Add author bios (optional)

An author byline may be enough for you, in which case, there’s no need to read the rest of this article.

But if you want an author bio to appear on your articles, read on!

We could just put Henry’s bio in the front matter of each of his articles—but then we’d be copying and pasting all the time, which is not the best way to organise your code. Instead, we’re going to keep our code DRY. DRY stands for Don’t Repeat Yourself, and is a well-known concept in programming.

To that end, let’s create Henry’s bio in just one place, and pull from there whenever we need it.

To do that, we’re going to set up author data.

We’ll create a folder called authors, which will live in the content folder.

In that authors folder, we’ll create a folder for Henry:

/content/
󠀠󠀠󠀠󠀠 /authors/
  /henry-mystery/

Next, we’ll create a blank _index.md (note the leading _) and put it in Henry’s folder:

/content/
󠀠󠀠󠀠󠀠 /authors/
  /henry-mystery/
󠀠󠀠󠀠󠀠 󠀠󠀠󠀠󠀠 󠀠󠀠󠀠󠀠 _index.md

Inside this newly-created _index.md file (which lives at /content/authors/henry-mystery/_index.md), we’ll put all of Henry’s details. It will be a file exclusively comprised of front matter. If you’re using YAML, it would look something like this:

---
name: "Henry Mystery"
bio: "Henry may or may not be an MI6 agent."
twitter: "HenryMystery"
---

Note that the above is not an excerpt; it’s the complete contents of the file.

The great thing about this setup is that if you need to change Henry’s bio (“oh, he actually works for the CIA?"), you only have to do it one place. No more bulk find-and-replace.

To actually add the bio and Twitter link to Henry’s articles, we need to add the following code after the article itself in /layouts/_default/single.html:

{{ range .Param "authors" }}
	{{ $name := . }}
	{{ $path := printf "/%s/%s" "authors" ($name | urlize) }}
	{{ with $.Site.GetPage $path }}
		<h2>{{ .Params.name }}</h2>
		<img src="/img{{ $path }}.jpg">
		<p>{{ .Params.bio }}</p>
		<p><a href="https://twitter.com/{{ .Params.twitter }}">Twitter</a></p>
	{{ end }}
{{ end }}

So what does that code do?

Well, it’s iterating (using range) over the array of authors (.Param "authors") on any given post. Again, we only have one author ("Henry Mystery") in our array, but that’s fine.

Next, it creates and assigns the variable $name to each author, which is represented by the .. In our example, $name is equal to "Henry Mystery".

After that, in the with block, we tell Hugo to use data (.Params.name, .Params.bio, .Params.twitter) from the /authors/henry-mystery/_index.md file.

I’ll come back to Henry’s headshot (photo) in a minute.

Note that we don’t need to specify the _index.md part of the path; this is built in to the .GetPage function already.

Also, the urlize function is where we turned "Henry Mystery" into the henry-mystery you see in /authors/henry-mystery/_index.md.

In other words, we lowercased and dash-separated Henry’s name. We did this with Hugo Pipes, which you can read more about in the official documentation.

Finally, we publish Henry’s details on his article.

His name goes inside h2 tags; his bio goes inside p tags.

Author images / headshots

The image code is interesting. I could have simply specified something like img: "henry-mystery.jpg" inside of his _index.md author data file, and then used it in a similar way to the other fields. That would have worked perfectly well.

However, I want to fill out as few fields as possible each time I create an author.

To do this, I’m going to create a naming convention for author images, and require that they be of the form /static/img/authors/firstname-lastname.jpg.

If I know that’s where my image will live, I can then simply reuse the $path variable I created earlier in the code block above.

In Henry’s case, the $path variable is equal to /authors/henry-mystery.

So all I need now to get my image path is to prepend /img and append .jpg.

Thus, I get /img/authors/henry-mystery.jpg, which I use in the <img> element in the code above.

The rendered article will look something like this, in code form:

<h1>Best cigar stores in Venice, Italy</h1>
<span>Henry Mystery</span>

<p>EVERY good secret agent needs a cigar.</p>
<!-- blah blah, end of article -->

<h3>Henry Mystery</h3>
<img src = "/img/authors/henry-mystery.jpg">

<p>Henry may or may not be an MI6 agent.</p>

<p><a href="https://twitter.com/HenryMystery">Twitter</a></p>

Step 5: Add a page that lists all your authors (optional)

This step is helpful if you’d like to get your site into Google News.

One of the requirements to get your site into Google News is that you have a page showing all your authors, to prove that you are a multi-author site, rather than just a solo blogger.

Here’s how you create your “list of authors” page.

First, let’s make the template. Create a file named author.terms.html inside the /layouts/_default/ folder:

/layouts/
󠀠󠀠󠀠󠀠 /_default/
󠀠󠀠󠀠󠀠 󠀠󠀠󠀠󠀠 author.terms.html

You must name your file author.terms.html—anything else won’t work.

Inside author.terms.html, include your normal header and footer.

For example, you could include your header like this:

{{ partial "header.html" . }}

which could be followed by the h1 heading:

<h1>Authors</h1>

Next, we need the logic to fetch the details of each author and print it to the page:

{{ range .Data.Pages }}
	<h2>{{ .Params.name }}</h2>
	<p>{{ .Params.bio }}</p>
{{end}}

By the way, for the sake of simplicity in the code above, I skipped the author image and Twitter link.

Anyway, this code is very similar to our code from earlier, where we created the author bio and other details in the article footer.

This time, however, we’re iterating (range-ing) over .Data.Pages instead of .Param "authors".

This is because we need to tell Hugo to look in the /content/authors folder (as opposed to the authors front matter field on a given post).

Step 6: Create a page for each author listing their articles (optional)

In this step, we’ll give Henry his own page that automatically lists all his articles.

First, let’s make the template for this. Create a file named author.html inside the /layouts/_default/ folder:

/layouts/
󠀠󠀠󠀠󠀠 /_default/
󠀠󠀠󠀠󠀠 󠀠󠀠󠀠󠀠 author.html

Inside author.html, include your normal header and footer, in the same way we did in Step 5, above.

The process is quite similar to when we created the “list of authors” page in the preceding step.

First, let’s populate the page with the author’s name and bio:

<h1>{{ .name }}</h1>
<p>{{ .bio }}</p>

Again, for simplicity’s sake, I’ve omitted the author image and Twitter link in the code above.

Now, let’s create a list of their posts:

<h2>Articles by {{ . name }}</h2>

{{ range .Pages }}
	{{ .Render "li" }}
{{ end }}

This uses the same /layouts/_default/li.html template we use on the homepage list of articles.

And we’ll end up with something like this between the header and footer:

<h1>Henry Mystery</h1>
<p>Henry may or may not be an MI6 agent.</p>

<h2>Articles by Henry Mystery</h2>

<h3><a href="/venice-cigar-stores/">Best cigar stores in Venice, Italy</a></h3>
<p>Want to know the best places to buy cigars in Venice?</p>

<!-- more articles by Henry -->

Wrapping up

So as you can see, there’s a fair bit to implementing author bylines, bios and Twitter links in Hugo. It’s very much worth it though, as you only have to enter each author’s information in one place, which means you can update it effortlessly. Creating the authors page also means you comply with that requirement for appearing in Google News.

Good luck!

"Thanks so much for your work ... I'm migrating my WordPress blog to Hugo and it's been really helpful" — Francisco S., engineer and blogger

The planets in our solar system