Migrating to Hugo + blogdown

Introduction

So my shiny new Hugo/blogdown website has been up for two weeks now without me having posted anything about it, which goes to show that this second tweet:

was maybe a little optimistic. But better late than never! In this post, I’ll be discussing why I switched, how I got started, and a few key pointers that helped along the way. This will probably be somewhat disorganized given that I’m writing it in a fairly stream-of-consciousness fashion; you’ve been warned.

(Note, however, that this post will not be a step-by-step tutorial on how to create a website with blogdown – much better versions of that already exist, including the official blogdown guide by Yihui Xie, Amber Thomas, and Alison Presmanes Hill, as well as this great quick-start blog post by Mike Treglia.)

Why switch?

I was previously using a Jekyll website hosted on GitHub Pages; more specifically, a very lightly modified version of Dean Attali’s Beautiful Jekyll theme. I have no complaints with the theme itself – it’s super nice to look at and was very straightforward to get started with. But there were a few things I wanted that Jekyll alone could not provide:

1. The ability to write blog posts in R Markdown

This might seem like an oddly specific need, so context is probably helpful. For the past two years, I’ve had the fortune of co-teaching a third year R course called Quantitative Methods in R for Biology with the EEB department here at U of T. The lecture material is developed entirely in R Markdown and released on the course website for students to use as reference in class, since all material is live-coded following the teaching model employed by The Carpentries.

The course website, developed primarily by my wonderful friend and colleague Luke Johnston, renders these R Markdown lectures beautifully and generates clean HTML pages on the website. Working with this website made me realize the added utility the Rmd format offers in writing technical/scientific documents in general, and I realized that that was something I was just not going to get out of Jekyll.

1.5. Being able to execute code in a blog post and display the output

If you’ve ever used R Markdown, this point follows from the first, but I’m still writing it out separately to emphasize its usefulness. R Markdown allows for executable ‘code chunks’ to be interspersed in a plain text document, which is very useful for both creating lectures and reproducible scientific documents in general.

When it comes to blog posts, however, the ability to write code and have its output automatically generated below is monumental if one is to write any form of tutorial or technical post. Couple that with the fact that R Markdown supports code chunks in other languages (Python, Bash, JS, etc), and the scale of this feature’s utility becomes apparent.

Here’s some Python:

l = [1, 2, 3]
for i, num in enumerate(l):
  print('currently at number {}'.format(num))
## currently at number 1
## currently at number 2
## currently at number 3

some Bash:

for i in {1..5}; do echo $i; done
## 1
## 2
## 3
## 4
## 5

and, of course, some R:

library(magrittr)
library(broom)
mtcars %>% 
  lm(mpg ~ wt, data = .) %>% 
  tidy()
## # A tibble: 2 x 5
##   term        estimate std.error statistic  p.value
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)    37.3      1.88      19.9  8.24e-19
## 2 wt             -5.34     0.559     -9.56 1.29e-10

Note that in all three of these instances, I’m simply typing the relevant code into an R Markdown code chunk and swapping out {r} for {python} etc as needed. The output is generated by blogdown, which runs every code chunk in the Rmd file before rendering the whole thing as HTML. I have yet to leave RStudio at any point during this process. Pretty neat!

2. LaTeX math mode support in blog posts

While I admittedly still feel like I didn’t poke and prod at Jekyll enough to make this work, I really, really wanted some form of math mode support on my website. LaTeX (via Overleaf) is my primary writing tool, and I’ve grown very accustomed to using math mode to efficiently write out mathematical expressions.

On our EEB313 course website, anything written in math mode in a lecture’s Rmd renders perfectly on the website (see this fantastic lecture on mathematical modelling by Amber Gigi Hoi and Zoe Humphries, who I was fortunate to teach the course with this year). This was something I really wanted for myself; on my original website, writing out population genetic statistics such as \(\theta_{\pi}\) and \(F_{ST}\) required copy and pasting in special characters and using HTML tags like <sub> for subscripts. Really, really not fun.

In retrospect, given what I had to do to enable math mode for my Hugo website (more on that below) I could have probably done the same for my earlier website, so I don’t want to knock the latter at all for this point. But it does feel good to be able to write math right alongside executable code chunks!

3. Doing everything from RStudio

If you’ve ever had the misfortune of asking me about my long-winded opinions on IDEs and such, you’ll know I’d generally be hard-pressed to name anything that I believe beats the simple efficiency of vim and tmux – and thus refuse to use anything else. The only exception to that remains RStudio, which not only does every single thing right, but is also maintained by an excellent team that is constantly adding relevant and useful features.

I particularly love RStudio’s projects feature, with also integrates beautifully with Git/GitHub and keeps everything nice and self-contained. The prospect of maintaining the many disparate components of a website (blog posts, about/CV pages, layouts, etc) alongside added functionality from blogdown (widgets for creating new posts, inserting images, etc) all within a single RStudio project was, naturally, pretty appealing.

Getting Started

I started where anyone else wanting to start with blogdown should – with the official guide that I linked above (and will again here). The guide covers how to get started with the blogdown package as well as Hugo, the static site generator with which blogdown does its thing. Bear in mind that although the guide is generally structured in the order of steps you’d take to build and deploy a website, it’s quite heavy on details as opposed to just being a straight step-by-step tutorial and nothing but. This is obviously a good thing and makes the book a great reference, but if you’re an impatient soul like myself, you’ve been warned!

At the very least, I still recommend going through all of Chapters 1 and 2, and picking your theme (section 1.6) at some point along the way. There are a ton of great themes out there, but it’s worth keeping in mind that there are often slight differences in the config.toml files between them. This means that a config setting in one theme may not be the same as another (i.e. syntax highlighting might be enabled with highlight = true in one theme, but highlightjs = true in another).

Once you’ve chosen a theme, RStudio’s own New Project dialog actually includes a ‘Create website using blogdown’ option, which allows you to simply list the Git repo hosting a given theme and have RStudio import it in full. I opted for the hyde-hyde theme by htr3n as a starting point, because a) it was nice and minimalistic and b) I was very, very adamant about wanting a nice looking sidebar.

I’d recommend really exploring every nook and cranny of your newly downloaded theme before making major modifications or starting to migrate your own content. I talk a bit more about the directory structure (which was admittedly trickier to figure out than I’d have liked) below.

Note that the website can be deployed locally from within RStudio (or the Terminal – more on that below) at any time. Not only is this unbelievably useful to preview changes (especially if/when you’re initially modifying the aesthetics of the theme to suit your needs), it in fact updates instantaneously every time a change has been made. I can’t stress just how much of a time saver this has been.

And now for some smaller tips on hurdles I encountered along the way! Please note that these are more pointers than tutorials, and the reference material I’ve linked and will link along the way will be substantially more useful for getting a base understanding of what’s going on. I’m also not really going to avoid jargon too much – I’m envisioning these tips to be useful once you’ve already got a theme loaded, have done some exploring/reading, and are now getting into the creation/migration process.

Some tips

Hugo’s directory structure

Hugo’s directory structure might also be a bit strange to newcomers, though I found this blog post to be quite helpful in demystifying why it is the way it is. Another good rundown is naturally found in the blogdown book, in section 2.5.

I won’t recap the post/section, but here are some key things to keep in mind – some of which I made the mistake of discovering via slow and somewhat frustrating trial and error out of some weird initial need to just figure this out on my own without resorting to the reference material.

  1. The website is rendered from the public/ directory.
  • A corollary to this is that public/ should be treated as read-only.

  • Of course, you’re able to edit any file in public to your heart’s content. But know that every single time your site is regenerated (which happens after even the smallest changes) whatever edits you directly made in public will be overwritten.

  • Basically - do not even bother entering public except for when you’re pushing the website to GitHub for deployment (more on that below). All the action happens in other directories/files.

  1. Your layouts/ and static/ directories will most likely be empty at first.
  • Why does this matter? Well, Hugo uses the templates in layouts/ to render the markdown files in your content/ directory. Can’t do much of that if there are no layouts.

  • However, if your theme is anything like mine, there ought to be a themes/ directory containing its own layouts/. Copy everything in the latter former into the base layouts/ folder. Same goes for static/.

  • I don’t know if this is standard practice, but it worked for me, and only needs to be done once, really.

Two ways to get your site running locally

The blogdown book recommends using blogdown::serve_site to create a local server, but I’ve found that running hugo server in Bash while in the root directory of your site does the same thing. The advantage of the latter is that it’ll reload when you make purely cosmetic changes, which I found useful when initially making some sweeping design edits. Conversely, I found serve_site didn’t always respond to those changes, for whatever reason. This may be one of those (frequent) instances where I’m just doing something terribly wrong, so your mileage may vary.

One clear difference between the two (and one I’m actually quite sure of!) is that if you’re editing an Rmd file, a server via hugo server will not dynamically update the post. This is because as far as I can tell, the Rmd files are not rendered by Hugo, and only serve_site actually looks for changes in your Rmd files before re-rendering any changed files as html. This makes intuitive sense to me, since hugo isn’t explicitly built to pull up an R kernel and selectively run chunks of code in a text file before rendering as HTML, whereas blogdown is all about compiling those Rmd files.

Finally – and this isn’t explicitly about getting a local server going, but has been helpful – running either of blogdown::build_site in R or just hugo in Bash while in your root dir will make Hugo rebuild your site. Useful in a pinch, and my goodness is it fast.

Adding your own parameters

Post-specific parameters

One challenge I faced when putting together the Projects page involved designating a given project as having been from my undergrad years. In my case, I wanted to do this because I wanted that post to render in a separate section in my Projects page.

In the hyde-hyde theme, each project in the Projects page is rendered from a separate md file. In the md file pertaining to this undergrad project, I added the following in the YAML header:

undergrad = true

Then, in layouts/partials/projects/content.html, the parameters can be accessed via .Data.Pages, with this parameter in particular via Params.undergrad. To be honest, I’m still not super comfortable with the syntax, but I fiddled with it enough to get it working this one time so that’s about the extent of what I can share.

    <h2>Undergraduate Research</h2>
    <section>
        <div class="portfolio_content">
            <hr class="divider">
            {{ range (where .Data.Pages "Params.undergrad" "==" true).ByDate.Reverse }}
            <div class="row">
                <div class="col-md-4 col-sm-4 col-xs-12" href="{{ .Params.link }}"
                    target="_blank" rel="noopener noreferrer">

The specific thing to look for is that line wrapped in pairs of curly braces. What this does is render all pages where undergrad == true, and then sort them by date with most recent first. Removing the ByDate.Reverse bit would just render the range of posts fulfulling that criterion. This is a modification of code that was already in this file as part of the original theme, but works for me just fine. There’s tons more documentation to be found on the Hugo docs if you’d like to do some reading of your own.

Global parameters

You’ll find a whole host of parameters listed in your theme’s config.toml, and it’s likely they’ll cover most if not all of your needs. However, should you feel the need to add your own, I found adding them to be quite intuitive.

To do so, you more or less add whatever parameter you want to bring into existence to config.toml and then modify your theme’s partials accordingly. Say there was a chunk of HTML code in one of your partials you wanted to have the option of hiding globally.

<p>I am a chunk of stuff you want to have the option of hiding globally.</p>

(I’d have a better example if I was actually competent at HTML)

First, in config.toml, add a new parameter under [params] and set it to some value (boolean/character/numeric). In this case, I’ll call this parameter showchunk:

showchunk = false

Then, modify the HTML as follows:

{{ if .Site.Params.showchunk }}
  <p>I am a chunk of stuff you want to have the option of hiding globally.</p>
{{ end }}

And now you can enable/disable parts of your HTML at will using .Site.Params.

Math mode (MathJax) support

So, some not great news – most Hugo themes don’t natively include MathJax support, which means that writing, say, the expected genetic diversity in a population tends to render like this:

\[ \theta = 4N_{e}\mu \]

instead of \(\theta = 4N_{e}\mu\). Fixing this was a bit frustrating, but the solution I eventually found (via this great blog post) boiled down to:

  1. In layouts/partials, create a file called mathjax_support.html containing the second snippet of code in the above blog post.

  2. The post above recommends including the file in header.html or footer.html. Maybe it’s my theme, but I had little success with either. In my case, it was adding it to layouts/partials/page-single/content.html that did the trick. By ‘include’, I mean add the following snippet:

{{ include mathjax_support.html }}

In line with the tip above about adding your own parameters, I nested this within a parameter I added:

  <div class="post">
    {{ if .Site.Params.math }}
      {{ partial "mathjax-support.html" }}
    {{ end }}
    {{ .Content }}
  </div>

This way, I can set math = false in config.toml if needed.

Note that the hugo-academic theme comes with MathJax support out the box, and is generally a great theme to use if you’re an academic. “But you’re an academic and didn’t use it,” I hear you cry, and you’re right – but like I said earlier, I was very adamant about having a nice looking sidebar.

Pushing to GitHub

This is explained in the blogdown book (section 3.3) but I completely missed it at first, as expected. The takeaway is this: When pushing your website to GitHub, init your repo in public/, not in your website’s root. Commit everything in public/, push, and render with GitHub Pages; public/ should contain everything GitHub Pages needs to put the front-end together.

You may want to commit your whole site directory for posterity, but creating a Git repo that contains a subfolder which is itself a separate Git repo is a much murkier prospect than it sounds: if you’re currently in public/ and run a Git command, what’s stopping Git from thinking you’re in a subfolder of the root directory and acting accordingly? Although workarounds are possible, I just went with the following archival method:

  1. Run git init in the root dir.
  2. Commit and push to GitHub.
  3. rm -rf the .git folder, essentially un-making this directory as a repo.

It’s an ugly workaround, but it essentially creates a time capsule on GitHub of what the rest of your site – config.toml, your layouts and static folders, etc – looks like. I figured it’s unlikely I’ll make many major changes to it, so this at the very least means that my site code lives outside of just my computer.

If you’d like to go the whole nine yards with maintaining nested Git repos, the method is laid out quite nicely near the bottom of this blog post.

Conclusion + Reading List

This post is already quite long, so I’ll try to keep this brief. I’m having a ton of fun tinkering with blogdown, and I don’t see myself switching away from it anytime soon. If you’re thinking about doing the thing, I recommend doing the thing; it’s much less daunting than it might seem, and a fun side project to boot.

Reading List: