The Jekyll site generator is a simple way to create static pages. It runs in Ruby and makes use of markdown-like style and of Liquid-based templates. Jekyll is integrated with GitHub, so different themes and gems can be chosen.

Local development

Install and run

The installation process expects a Linux or OSX environment, and some packages such as Ruby and make.

The files can now be built and served locally by a thin server. The --incremental flag allows to re-build just the content modified. The result of the building process will be available in the shell where these commands are executed.

1
2
jekyll build
jekyll serve --incremental

Set up the structure

Starting a site with Jekyll should start with the following items, in order:

  • Definition of _config.yml at the root
  • Adding the main file index.html at the root
  • Providing content for the posts under _posts, at the root

After those are all set-up, the basic structure for the site is all set-up. From that point you can start filling with specific styles and layouts, use common includes files or static pages. An example of a working tree with all the aforementioned would be as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.
├── _config.yml
├── _includes
│   ├── footer.html
│   ├── header.html
│   └── sidebar.html
├── _layouts
│   ├── default.html
│   ├── page.html
│   └── post.html
├── _pages
│   ├── 404.html
│   └── feed.xml
├── _posts
│   └── post-example.md
├── section1
│   ├── subsection1
│   │   └── index.html
│   └── subsection2
│       └── index.html
├── css
│   └── default.css
├── img
│   └── background.png
├── index.html
└── robots.txt

Some of these sections are part of the default structure:

  • _includes: where chunks of code can be imported into bigger templates
  • _layouts: gathering the layout or theme files per view
  • _posts: blog posts shall be kept here, where these are directly generated into html files

Some others can be defined by the user, such as _pages (to include any section or page), $section (root-level section, acting as a category) or multiple folders to keep stylesheets, images or scripts.

The _config.yml file

The configuration file can be used as a central registry for variables to be used across your pages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Setup
title:        "Title of your site"
description:  "Mid-sized description of your site (to be used e.g. for feed or meta description)"
tagline:      "Short-sized description of your site (to be used e.g. within the site"
url:          /url/to/github/pages/site

# About/contact
author:
  name:       "Your name"
  url:        /url/to/personal/site
  github:     "Your GitHub ID"

# Gems
gems:
  - jekyll-paginate

# Include extra folders
include:
    - non_default_section_to_include_from_root

# Markdown parsing
markdown: kramdown

The default values added within Jekyll are defined here. Many other configuration values are available for purposes such as defining the permalink format, customise pagination for posts, adding plug-ins or applying themes.

The main file

The front page is a normal index.html or index.md file, placed at the root of your repository. A layout can be defined, based on the different view to be provided; such as page for static pages or post for blog entries. These are located under the _layouts folder and are widely customizable by the user, though. Adding such tag in the header of the post’s Markdown file will result in Jekyll applying the post’s layout to it.

In this case, the default layout will be used – as it contains specific code, relevant only to the index.

1
2
3
4
---
layout: default
title: 
---

The content comes right after such header in every page using the Liquid templating system.

The posts

Now it’s time for the content itself. The posts can be defined in Markdown language or directly prepared using HTML. In the former case, these will be converted by Jekyll into the end HTML files. The post files must be placed under the _posts folder. When building and generating the HTML content, these posts will be placed into appropriate folders, depending on the publish date or other data such as categories. Here you can find guidelines and details.

The name

The files shall be named in the format year-month-day-title.md. Note that hyphens (-) are allowed, but others as underscore (_) will preclude the building and generation of html files.

The header

Similar to the main file’s view, the post’s view can use a specific layout as well. More useful values can be assigned to define the title, publish date, or the categories and labels to further organise the content.

1
2
3
4
5
6
7
---
layout: post
title:  "Title of your post"
date:   2010-01-01 00:00:00
categories: category1
tags: [tag1, tagn]
---

Enhance it

The basics are all working now. Yet there are many other features around. Just to name a few:

Pretty URLs

If you would like to avoid .html in your URLs (whether it is part of the post or a section itself), do have the following in mind:

Section URLs

Instead of defining a sectionname.html file, do create a folder and an index file such as sectionname/index.html. You will just need to point to sectionname/ (note the ending slash!)

Post URLs

In this case, you need to remove the HTML termination from the post files for every link that points to the content. This is a post-processing done in the URL of the post in order to work as a pretty URL (remember to remove spaces between the curly braces):

1
{ { post.url | remove: '.html' | prepend: site.baseurl } }

Excerpts

When listing the available posts in a single page, or in an RSS feed, you can just provide the excerpt of the post (remember to remove spaces between the curly braces):

1
{ { post.excerpt } }

Finer control is allowed, as the excerpt_separator key can be defined in _config.yml to define a specific value that identifies the end of the excerpt:

1
excerpt_separator: <!--more-->

Table of contents

These are quite useful for lengthy posts. Adding the following right after the post’s header will do:

1
2
* TOC or any other text can be added (will not be shown)
{:toc}

RSS feed

Original source from Jekyll tips. This is a simple iterator on the available posts that also provides details on the site as acquired from the configuration file. The code will largely resemble this (remember to remove spaces between the curly braces or %s):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
layout: null
---

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{ { site.title } }</title>
    <description>{ { site.description } }</description>
    <link>{ { site.url } }</link>
    { % for post in site.posts % }
      { % unless post.draft % }
        <item>
          <title>{ { post.title | xml_escape } }</title>
          <description>{ { post.excerpt | xml_escape } } (Read more...)</description>
          <pubDate>{ { post.date | date_to_xmlschema } }</pubDate>
          <link>{ { post.url | prepend: site.url } }</link>
          <guid isPermaLink="true">{ { post.url | remove: '.html' | prepend: site.baseurl } }</guid>
        </item>
      { % endunless % }
    { % endfor % }
  </channel>
</rss>

Comments

Adding Disqus in the Jekyll posts can be done in two steps:

  1. Create a new page for comments, for instance at \_layouts/comments.html, with the code at the Universal Embed Code.
    • Note that it is suggested to update the Javascript variables conveniently – check this) and this for specific details to replicate this
    • Summing up, you need to include a disqus directive in your configuration file to define your Disqus ID, then read this and the specific URLs from the environment variables
  2. Modify the layout (e.g. the default one) and the posts you would like to enable comments on
    • Use “true” or “false” to your convenience, both in the layout and the posts pages
      1
      2
      3
      4
      5
      
      ---
      layout: default
      comments: true
      ...
      ---
      
    • At the end of the page add a snippet to include the comments’ file (remember to remove spaces between the curly traces)
      1
      2
      3
      4
      
      ...
      { % if page.comments % }
      { % include comments.html % }
      { % endif % }
      

Formulas

This requires the MathJax Javascript library. Different methods can be found here and here (see running example). The second method was used for this site.

Includes and sections

Note that, if you want to reuse your code to the maximum, you may want to place partial HTML files under the _includes folder. You can then just use the Ruby’s include directive as follows (remember to remove spaces between the curly traces):

1
{ % include partial-html-file.html % }

Other sections can be added to work in the same manner as the default ones (_includes, _posts, etc). To do that, add one section per line in _config.yml:

1
2
include:
    - sectionname

Themes

In GitHub you have the possibility of extending from a list of available themes that you can define in your _config.yml file. _Be careful, however, as the theme you pick i) will work only in remote – if you want to work you will need to download the specific theme into your root’s folder, and ii) the remote theme customisation can be tricky, as you will be overriding the default theme by extending HTML and CSS specific files into pre-defined folders in your repository.

Plug-ins

It is also possible to provide plug-ins, both for Jekyll or for its different dependencies, like the Rouge highlighter. A practical example is to attempt to override the Rouge’s formatters to avoid HTML tables and use divs instead.

First you need to identify which version you use for both Ruby and the module you want to override. In this specific example, using Ruby 2.3.0 and Rouge v1.11.1 on an OSX system, the code to override is located at /usr/local/lib/ruby/gems/2.3.0/gems/rouge-1.11.1/lib/rouge/formatters/html.rb. You can find the location in your system by issuing gem which rouge.

The class above will serve as the base. Create a new _plugins folder, located at the root of your pages’ directory. Inside the folder, place a copy of the code to override. Before starting any modification, you will need to include the specific Ruby’s module (here, “Rouge”) – were this not present, Jekyll would not be able to identify the Rouge module, nor any of its submodules and classes. Note the “require” at the first line.

1
2
3
4
5
6
7
8
9
10
11
require 'rouge'

# -*- coding: utf-8 -*- #
# stdlib
require 'cgi'

module Rouge
  module Formatters
    # Transforms a token stream into HTML output.
    class HTML < Formatter
    ...

Once that’s done, you can proceed as you’d normally do with the code modifications.

Note: user-plugins are not enabled during deployment to GitHub; so for them to work you need to directly publish your sources.

Upload the content

You can now upload the content of your site to your GitHub pages repository and these will be online few seconds after you push the code.