Setting up a blog using blogdown

For this blog you are reading now, I decided to go with Jekyll and to use .md files. This was very easy as I only had to fork an already organized repo from barryclark/jekyll-now that had a theme that I liked. However, at work I wanted to set up a blog using RStudio’s blogdown which uses Hugo, even though the upfront setup time takes longer. I was particularly interested in this methodology because it allowed me to simply convert my R Notebooks into blog posts, without having to save (and probably re-save plots) and refer to the location of the png files.

The blogdown package utilizes Hugo for blog generation, as opposed to Jekyll. However, the creater of blogdown claims that migrating a blog from Jekyll to Hugo is as simple as finding a new theme and updating the YAML metadata. Perhaps I will switch this blog over to Hugo if I decide I really want to be able to reproduce plots with ease.

blogdown

Because there are many sources devoted to describing how to use blogdown to generate a website, I will not go into a level of detail that would allow the reader of this blog post to generate their own blog. However, I will attempt to direct to the reader to the resources I found helpful and will fill in the gaps where I felt the resources fell short.

Setup

My first attempt to set up a blog utilized this blog post. This blogger went the first of two potential avenues to integrate Hugo with Github Pages:

  1. Have one repository with a default branch named sources and a master branch that only contains the content of the public/ folder, the only files Hugo will allow to generate the blog.

  2. Have two repositories, each with only a master branch. The first I have called blog_setup which contains the R project and all of the files generated by blogdown. The second is called blog (for the sake of a nice url), which contains only the contents of the public/ folder from the blog_setup repository.

Single repository method

While Amber’s blog was enormously helpful, I felt it was the more difficult approach for many reasons:

  • Setting up both the setup.sh and deploy.sh scripts made the process of setting up Github and deploying the site more of a black box than the two-repository alternative. If running the setup.sh file failed, I was left without a master branch and found myself having to start over.

  • It made it harder to revert to old versions or to branch on Github because the master/sources branching structure could not be altered.

Two-repository method

The next blog post I read detailed the two-repository method, which I found much more graceful to work with. The following is a summary of the steps you would need to take to build a blog in the same fashion:

  1. Set up a blog_setup and a blog repository on Github. Clone the repos to a directory on your machine. Make sure to set up Github Pages on your blog repository through the Settings tab.

  2. Create an R project within the blog_setup directory of your local machine. Open it, install the development version of blogdown using devtools::install_github('rstudio/blogdown'), and load the library. Then use the new_site() function to populate your blog_setup folder with the necessary files and folders.

  3. Select a Hugo theme. This can be tricky, because some themes don’t play so well with blogdown. I initially liked the “Bootstrap Premium” theme, but discovered it became very difficult to work with because it had functionality for multiple languages. I ended up settling on “Hugo Bootswatch” because it has a simple interface and very basic config.toml file. Install your theme with the Github link as follows: install_theme("nilproductions/hugo-bootswatch", theme_example = T, update_config = T). This will update your site files to adopt your chosen theme. Note: I didn’t have much success “switching” between themes. It seems blogdown can handle it, but the config.toml file adapts components of both themes which didn’t help when I had the overly complex config.toml from the Bootstrap Premium theme.

  4. Edit your config.toml file.
    • Change the baseurl to the site you are publishing to. For me at work, this was "https://github.comverge.com/pages/jmaddalena/blog/"
    • Change the publishDir to your blog repo. Using the “~/” shortcut does not work properly!!
  5. To build the site and send the contents of your public/ folder to your blog directory, run build_site() within your R project. You can also run serve_site() to preview the site within R. If build_site() is ran first, serve_site() will be fairly quick to show the results of the build. However if serve_site() is ran first, build_site() still has to compile everything on its own.

  6. To publish your built website, you will need to push all of your new content to GitHub. Remember, you will have to do so for both repositories (though blog is the only one that is required for the website to work). Once you have pushed to GitHub, you should be able to view your new blog at your specified baseurl.

Content

Once I got my blog up and running, I needed to alter the content to consist of my project documentation R notebooks, as well as Markdown files such as this blog setup post. I also needed to get under the hood a bit to adjust the interface.

Adding new posts

I was greatly disappointed when I realized blogdown does not support R notebooks. This meant I could not use interactive plots, code folding, and many other nice features that .Rmd files do not support. However, I was able to convert my notebooks to .Rmd files (which do knit to .html) and hide the code and other unwanted output with the following global options setup:

```{r global_options, include=FALSE}
knitr::opts_chunk$set(echo=FALSE, warning=FALSE, message=FALSE)
```

The blogdown package has a new_post() function that will automatically put new content, such as an R markdown file, in the content/post folder of your blog_setup directory. This can be handy, but you can also copy an .Rmd file from elsewhere on your computer and put it into the content/post folder. In doing so, you may have to change the front matter (YAML) to look like the following:

---
title: "Long-Term System Load Forecast Improvements"
author: "Julia Maddalena"
date: 2017-07-23T21:13:14-05:00
categories: ["R"]
tags: ["Belford", "Long-term", "Weather", "Epoch", "GBM", "Holidays"]
---

Including images

To include images that are not generated with code, place the image (preferably as .png) in the blog_setup/static/img/ folder. Images for all blog posts can live in this directory.

Customization

CSS

I had some issues with the default css of the Bootswatch theme when it came to tables. In converting from notebooks to .Rmd, I had to wrap all my table-generating code in the kable() function, the knitr package’s way to generate html tables. When I knit the function within R, the tables look nice and default to taking up the entire div width. However, when I built or served the site, the tables looked very scrunched together with no horizontal padding within the columns. I ultimately realized this was one of the seven .css files in blog_setup/themes/hugo-bootswatch/static/css that was causing the padding in my tables to disappear.

Rather than attempting to find the problem across seven files, it is much easier to create a custom .css file whose contents will override any contradicting code in the other .css files. This can be done by creating a custom.css file and placing it with the other .css files in themes/hugo-bootswatch/static/css. Then, to tell Hugo to use this file and to prioritize it over the other style sheets, open themes/hugo-bootswatch/layouts/partials/header.html and add <link rel="stylesheet" href="/css/custom.css"> below the other stylesheet links.

Reference: Custom CSS

Using LaTeX Typesetting

If your post includes math formulas using LaTeX syntax, e.g. $\frac{-b + \sqrt{b^2 - 4ac}}{2a}$ , you will need to load the mathjax javascript library by placing a snippet of text in your headers partial (e.g. blog_setup/themes/hugo-bootswatch/layouts/partials/header.html) as described here.

Custom Summary

By default, Hugo uses the first 70 characters of a post to show as the summary of the content of each blog post. This rarely creates a good summary, as it combines the first 70 characters whether or not they are headers, table of contents, or other undesired text that begins the post. This post claims to be the solution to setting user-defined summary breaks in the content, but their solution only worked for the .Md files, not for the .Rmd files. To allow for a custom summary in my .Rmd files, I had to add a summary: taxonomy to the YAML of each post that I wanted to override Hugo’s default summary. In addition, to tell Hugo to use this summary if present in a post, I had to edit the blog_setup/themes/hugo-bootswatch/layouts/index.html file as follows:

Replace <p></p> with <p> </p> as described as a cumbersome solution here.

Caching

If no caching structure is in place for .Rmd files, the build_site() function will start to take longer and longer as the number of posts on the blog accumulate. I don’t recommmend setting cache = TRUE in the global options of your .Rmd file that has not been finalized, as any changes to the source code will not be recognized as long as the code chunk itself remains the same.

If you have set a document to cache but have made any changes to the document or the source code, I have found that setting cache = FALSE in just that document and re-building the site will update the changes. At this point, you can set cache = TRUE again, and build twice (to save and read the caches) to maintain cached changes.*

*Caveat: this has not produced consistent results for me. Caching seems to be very risky until the post is finalized and you’ve moved on to new things.

Conclusion

While the process of setting up a blog using blogdown, Hugo, and Github Pages was more difficult than I first anticipated, I am pleased to have gotten a blog up and running that behaves as desired. With .Rmd allowed as content, I can now reproduce blog posts with ease, without having to resave plot images. I can also make writing the blog post part of my workflow, testing functions that read and manipulte data, and generate plots or and tables, adding supportive text as I go.

Written on December 31, 2018