Home → The Classics → Farai's Codelab
Adding JSON Feed to My Hugo Static Site
Published: Updated:
I decided to implement JSON Feed on my website just for the hell of it. I do plan on having a better way of laying out my feeds but that’s for another day.
Configuring The Page For JSON Output
I first configured the home page to output JSON.
#config.toml
[outputs]
home = ["HTML", "RSS", "JSON"]
Making the Template
I then made the template. There were two parts to this– getting the feed’s information and getting the information specific to each entry.
Feed’s Information
To get the feed’s information, I did this.
<!--page.json-->
{
"version": "https://jsonfeed.org/version/1",
"title": "{{ .Site.Title }}",
"home_page_url": "{{ with .OutputFormats.Get "html" }}{{ .Permalink }}{{ end }}",
"feed_url": "{{ with .OutputFormats.Get "json" }}{{ .Permalink }}{{ end }}",
"description": "{{ .Site.Params.Description }}",
"author": {{ dict "name" .Site.Params.Author "url" .Site.BaseURL "avatar" (printf "https://www.gravatar.com/avatar/%s" (md5 .Site.Params.email)) | jsonify}},
"items": [
{{ $count := newScratch }}
{{ $count.Add "count" 0 }}
{{- range first .Site.Params.rssLimit (where .Site.Pages "Section" "blog") -}}
{{- partial "page.json" . -}}
{{- $count.Set "count" (add ($count.Get "count") 1) -}}
{{- if ne ($count.Get "count") .Site.Params.rssLimit -}},{{ end -}}
{{- end -}}
]
}
Note line 7 which is the author’s information, which consists of a name
, url
, and avatar
. Using the dict
and jsonify
function, I was able to turn a list of fields into a valid JSON object. That results in something like this,
"author": {
"avatar":"https://www.gravatar.com/avatar/6d2bd7cabb1639ba65863c24b4e6b87f",
"name":"Farai Gandiya",
"url":"https://fgandiya.me"
}
Isn’t in the order I would like, but it’s valid.
Also, in the items field, I had to create a $count
variable. What $count
does is make sure that the last element in the page list1 doesn’t have a trailing slash. I thought I could get away with it, but JSON doesn’t allow for trailing commas.
Item’s Information
As for each item’s information, I put the logic into a partial called page.json
.
{{ $info := newScratch }}
{{ $info.SetInMap "info" "url" .Permalink }}
{{ $info.SetInMap "info" "id" .Permalink }}
{{ $info.SetInMap "info" "title" .Title }}
{{ $info.SetInMap "info" "content_html" .Content }}
{{ $info.SetInMap "info" "content_text" ( .Content | plainify ) }}
{{ if isset .Params "description" }}
{{ $info.SetInMap "info" "summary" .Params.description }}
{{ else }}
{{ $info.SetInMap "info" "summary" .Summary }}
{{ end }}
{{ $info.SetInMap "info" "date_published" .Date }}
{{ if ne .Date .Lastmod }}
{{ $info.SetInMap "info" "date_modified" .Lastmod }}
{{ end }}
{{ jsonify ($info.Get "info") }}
Working off of how I made the author
object, I thought I would be able to create a dict
object and dynamically append to that. That didn’t work. After hours of Googling about “how to create an object in Hugo”, I got nothing. closest thing I found was a blog post on how to Build a JSON API With Hugo’s Custom Output Formats by Forestry.io.
From there, I picked up that I had to make a Scratch
. Using the .SetInMap
method, I was able to add each entry to a key called info
, that uses a map as its value. With that map, I could then build up the post’s information before I turned it into a JSON object with jsonify
.
The beauty of jsonify
is that it escapes everything that you need to escape. This is really handy when it comes to the content_html
field that uses .Content
which outputs HTML.
Made up of
.Site.Params.rssLimit
items set to 15 for my site. ↩︎