Whilst working on the blog guide, Stefanie Butland and I consolidated knowledge we had already gained, but it was also the opportunity to up our Rmd/Hugo technical game.
Our website uses Hugo but not blogdown1 to render posts: every post is based on an .md file that is either written directly or knit from an .Rmd file.
We wanted to provide clear guidance for both options, and to stick to the well-documented Hugo way of e.g. inserting figures.
We also wanted to provide post contributors with an as smooth as possible workflow to create a new post.
Working on this mission, unsurprisingly we learned a lot, and why not share our newly acquired technical know-how?
In this post I shall go through four things we learned about Rmd/Hugo, while trying to provide context about the why of our using them.
knitr hooks
Problem: Hugo has a nice figure shortcode with options such as width. How do we make R Markdown output these shortcodes from a code chunk, instead of the usual figure syntax? I.e. how to get2
{{< figure src="chunkname-1.png” alt="alternative text please make it informative” caption="this is what this image shows, write it here or in the paragraph after the image as you prefer” width="300” >}}
not

to appear – as the result of a chunk producing a figure – in the .md after knitting the .Rmd?
that reads options from the chunk, and uses options from the hugoopts named list if it exists.
The chunk
```{r chunkname, hugoopts=list(alt="alternative text please make it informative", caption="this is what this image shows, write it here or in the paragraph after the image as you prefer", width=300)}
plot(1:10)
```
produces
{{< figure src="chunkname-1.png” alt="alternative text please make it informative” caption="this is what this image shows, write it here or in the paragraph after the image as you prefer” width="300” >}}
in the .md file which is what we wanted.
Now, a bit later in our website journey, I had a quite similar question: Hugo has nice highlighting options for code fences. How to make R Markdown show R source code with such options? This time there was no need to ask anyone, searching the internet for the name of the right knitr hook was enough: our .Rmd has to feature a knitr source hook. More on that highlighting chapter another time, but in the meantime refer to our standard .Rmd.
Note that when writing a .md post instead of knitting an .Rmd file, authors can use Hugo syntax directly. And when adding figures in an .Rmd file that come from say a stock photos website rather than a code chunk, authors can also use Hugo syntax, granted they write the shortcode between html_preserve markers, see below the lines I used to add the crochet hook picture in the .Rmd producing this post.
Problem: Our advice to contributors including ourselves was to add their post under content/ but its images under themes/ropensci/static/img/ which is… not smooth. How do we change that?
Thanks to Alison Hill’s blog post about page bundles I learned you can use a folder to add both a post and its related images to a Hugo website.
What an improvement over adding the post in one place, the images in another place like we used to!
It’s much smoother to explain to new contributors5.
In Hugo speak, each post source is a leaf bundle.
In R Markdown, in the setup chunk, the option fig.path needs to be set to "" via knitr::opts_chunk$set(fig.path = "").
Hugo archetypes and blogdown New Post Addin
Problem: We link to our two templates from the blog guide, and explain where under content the folder corresponding to a post should be created, but that leaves a lot of work to contributors.
How do we distribute our .Rmd and .md templates?
How do we help authors create a new post without too much clicking around and copy-pasting?6
First of all, an important clarification: in Hugo speak, a template for Markdown content is called an archetype. A template refers to html layout. Not confusing at all, right?
We have stored both the templates archetypes for Markdown and R Markdown posts as directory based archetypes. The Rmd template is stored as index.md, otherwise Hugo doesn’t recognize it (thanks Leonardo Collado-Torres). Posts created using the template should however be saved with the usual .Rmd extension.
if (file.exists('~/.Rprofile')) {
base::sys.source('~/.Rprofile', envir = environment())
}
# All options below apply to posts created via the New Post Addin.
# to enforce leaf bundles:
options(blogdown.new_bundle = TRUE)
# to make blog the subdirectory for new posts by default:
options(blogdown.subdir = "blog")
# to help enforce our strict & pedantic style guide ;-)
options(blogdown.title_case = TRUE)
The todo list for a contributor is long but less tedious than creating folders by hand:
Enter a title, no need to worry about title case at this stage.
Enter your name if whoami wasn’t able to guess it.
Choose the correct date.
Enter a new slug if the default one is too long.
Choose “blog” or “technotes” as a Subdirectory from the drop-down menu.
Choose an Archetype, Rmd or md, from the drop-down menu.
Also choose the correct Format: .Rmd if Rmd, Markdown (.md) if md. Never choose .RMarkdown.
Ignore Categories.
Select any relevant tag and/or create new ones.
Click on “Done”, your post draft will have been created and opened.
The addin can also be used outside of RStudio.
If you don’t use that nice little tool, unless you use hugo new yes you’d copy-paste from a file and create a correctly named folder yourself.
ignoreFiles field in the Hugo config
Problem: How do we make Hugo ignore useless html from our knitting process without deleting said html by hand?
At the moment the output type in our .Rmdtemplate archetype is
In this post I reported on a few things our website work taught us about R Markdown (knitr hooks), Hugo (ignoreFiles, leaf bundles, archetypes) and blogdown (New Post Addin).
We’re still learning important concepts and tricks thanks to new questions by blog authors and to updates in the ecosystem8, we shall keep publishing such posts, stay tuned if that’s your jam!
But as you’ll see later we actually take advantage of that cool package: we recommend using blogdown’s New Post Addin; and we also mention blogdown::install_hugo() in the blog guide. ↩︎