Static site generators have grown significantly over the past few years. It’s fast becoming the way to build websites because of the huge influx of powerful static site frameworks (such as Jekyll) and ask any web developer: hosting static websites is quite literally the easiest thing in the world to do and it’s usually capable of handling as many visitors as you can throw at it.

The most popular ways to host static websites is Amazon S3 and GitHub Pages, with the latter being free. We are huge fans of GitHub Pages over at Major League Hacking and use it across some of our biggest websites because a) it supports Jekyll, b) it fits in our version control workflow well and c) we never need to worry about our sites ever being slow or down.

Problems of Advanced Jekyll on GitHub Pages

For a very long time, GitHub Pages has been so perfect for us and our needs. We even started to use it for websites that previously had a backend and now they were simply static websites that consumed a REST API that dealt with backend tasks. This worked very well for us.

However, we’ve recently ran into problems with using GitHub Pages when trying to write custom logic. GitHub Pages supports logic that is already written in Jekyll’s codebase and these selected plugins, but does not support the use of custom plugins which we needed to have.

Solving the problem

One of the initial solutions we had to overcome this was to store all of our Jekyll code on the master branch and have our generated static website on the gh-pages branch. That way we can generate the website locally and GitHub Pages is just responsible for serving the website to our visitors.

Another tool we use in our Jekyll setup is Gulp.js which is an automation tool. You can create a set of commands and use external plugins that will perform actions such as image compression, CSS minification, linters and more. It’s a very important part of our toolkit.

Anyway, there is this wonderful Gulp package called gulp-gh-pages which solved our problem by doing exactly what we mentioned above. It allowed us to create a command called gulp deploy that would a) build the website, b) have the gulp-gh-pages Gulp.js package take the build from the last step and put it on the gh-pages branch and c) push the gh-pages to GitHub automatically.

Here’s what the gulp deploy command looked like in our Gulpfile.js:

1
2
3
4
5
// ...
gulp.task('deploy', ['build'], function(){
  return gulp.src('./_site/**/*').pipe(ghPages());
});
// ...

Then, in our workflow, this was our command to push a new version live in production:

$ git push -u origin master && gulp deploy

Pretty simple, eh? Now we can write all the custom logic and code we wanted to without losing the simplicity of GitHub Pages. It’s something we now use across all of our sites.

Writing custom plugins on Jekyll

So what was the problem that meant we needed to use custom plugins in the first place? Well, when creating the MLH Sponsor Portal, we had a list of all of our sponsors and needed to sort them by priority. Given that our data was structured as a hash (read below) which isn’t supported by Jekyll’s built-in sort filter (to my knowledge), we had to write a custom filter.

So, firstly, we had our sponsors stored as a Jekyll datafile. That is basically a .csv, .yaml or .json file hosted in the _data directory in the root of your Jekyll project. You can then read that into your Jekyll project like you would any YAML front-matter variable. It’s really neat.

# _data/sponsors/major-league-hacking.yml
name: "Major League Hacking"
description: "The official student hackathon league."
priority: 1
  
# sponsors.md
----
layout: default
----

<h1>Sponsors</h1>

{% for entry in site.data.sponsors %}
  {% assign sponsor = entry[1] %}
  <h3>Sponsor Name: {{ sponsor.name }}</h3>
  <p>Description: {{ sponsor.description }}</p> 
{% endfor %}

The above reads out all the sponsors stored in the _data/sponsors directory, but it does not sort them by priority. That’s where Jekyll’s custom plugins come into play. All you need to is create a Ruby file in the _plugins folder - though every time you edit it you’ll need to restart your server (because we use Gulp.js that would be gulp instead of jekyll serve --watch).

Also: you may have realised that I used a weird line which is {% assign sponsor = entry[1] %}. The reason why that is the case is because site.data.sponsors is a hash, where each sponsor has a key-value pair. The key would be the name of the file (in this case, major-league-hacking) and the value would be the hash from the parsed YAML from above.

1
2
3
4
5
6
7
8
9
10
11
# _plugins/sponsors_filter.rb
module Jekyll
  module SponsorsFilter
    def sort_sponsors_by_priority(hash)
      array = hash.values
      array.sort_by { |item| item['priority'] || 100 }
    end
  end
end

Liquid::Template.register_filter(Jekyll::SponsorsFilter)

The code above would a) take the site.data.sponsors as an input, b) convert the key-value pairs to an array containing only the values, and c) sort them by their priority (and if they didn’t contain one, assume the priority was set as 100).

Now once we have saved this file and restarted the server, we can now write the following code for sponsors.md:

# sponsors.md
----
layout: default
----

<h1>Sponsors</h1>

{% assign sponsors = site.data.sponsors | sort_sponsors_by_priority %}
{% for sponsor in sponsors %}
  <h3>Sponsor Name: {{ sponsor.name }}</h3>
  <p>Description: {{ sponsor.description }}</p> 
{% endfor %}

That code will now sort sponsors by priority and because our sort_sponsors_by_priority filter has stripped out the key-value pair from site.data.sponsors, we no longer need to use the {% assign sponsors = ... %} code.

Pushing our Jekyll code straight to GitHub and having GitHub Pages generate our site will no longer work because of the custom filter we made, but because of the gulp-gh-pages package we’re now using we can generate the site locally with our custom filter and send that straight to GitHub.

Check out our boilerplate

We’re huge fans of Jekyll, Gulp.js and GitHub Pages over at Major League Hacking and in spirit of the open source community, we open sourced our internal MLH Site Boilerplate that was inspired by Minimill’s Project Template. Feel free to give it a spin in your next project.

Over the coming weeks, I’ll be writing more on Jekyll and how you can harness the power of it to build incredible static websites. In the meantime, if you have any questions about how I use Jekyll over at Major League Hacking, feel free to reach out by sending me an email!