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:
[UPDATE: Tweet redacted due to broken Twitter API.]
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.
- 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 inpublic
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.
- Your
layouts/
andstatic/
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 yourcontent/
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 ownlayouts/
. Copy everything in the latter former into the baselayouts/
folder. Same goes forstatic/
.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:
In
layouts/partials
, create a file calledmathjax_support.html
containing the second snippet of code in the above blog post.The post above recommends including the file in
header.html
orfooter.html
. Maybe it’s my theme, but I had little success with either. In my case, it was adding it tolayouts/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:
- Run
git init
in the root dir. - Commit and push to GitHub.
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:
- The official blogdown guide
- Getting Going with Blogdown & Hugo
- A great quick-start guide if you’re itching to get going
- Hugo’s directory structure, explained
- Setting MathJax with Hugo
- What is hard about blogdown?
- This is a useful forum discussion prompted by Alison Hill, one of the co-authors of the official guide - there are some useful bits of info + ways to avoid common pitfalls listen in there
- R Markdown: The Definitive Guide