Since I’ve been working on a static site generator called Spelt, I thought it would be useful to take a look at the internals of Jekyll and see how all its pieces fit together. As I was doing this, I realized that this information might also be useful for someone using Jekyll for their site, so I decided to write up my findings here.

Please note that I am not an expert on how Jekyll works. I haven’t actually written any code for it. I’m just an outside observer of the project. If I get anything wrong, please let me know. With the disclaimers out of the way, let’s dive in.

Where the magic happens

Within the Jekyll codebase, your site is, quite logically, represented by a Site object. The process method of this object is where most of the magic happens. When you run jekyll build, this is the method that gets called to kick off the build process. Let's see what this method looks like:

def process
  reset
  read
  generate
  render
  cleanup
  write
  print_stats
end

The authors of Jekyll have done a wonderful job of writing this method in a way that clearly and succinctly describes all the steps involved in building your site. This makes our job of figuring out how the pieces fit together a lot easier. By going through these steps one-by-one, we'll get a pretty good idea of what goes on inside the static site generator.

1. Reset

This step clears the cache and resets some internal variables.

2. Read

This is where the data for your site is read from disk and converted to Jekyll's internal data structures.

The main data structures representing the different components of your site are:

Document

Blog posts are represented as documents.

Page

Represents a page in your site. Markdown or HTML files (containing front matter) that are not blog posts become pages.

StaticFile

Any file that does not contain front matter is represented as a static file. Static files are simply copied over to the destination folder and are not processed by the rendering engine.

3. Generate

After the data structures for your site have been created, we move on to the generate step. This step involves looping over all of the generators defined for your site and calling generate on them.

A generator is an object which can be used to create additional content for your site. For example, a generator could generate blog posts for each picture in your Flickr photostream. As far as I could tell Jekyll does not define any default generators, which means this step is only relevant when you have defined generator plugins for your site.

4. Render

The documents and pages in your site are processed in the render step. This is arguably the core of the entire process. The rendering process loops over the documents and pages and performs the following steps for each of them:

1. Render document using Liquid

Jekyll uses Liquid as its templating engine. This rendering step parses the liquid tags and filters used in your templates and inserts the output in the document. For example:

{{ "# Title" | markdownify }}

becomes:

<h1>Title</h1>

2. Run output through converters

This is where Jekyll converts your Markdown or Textile formatted text to HTML. These two default converters are defined in the same way that converter plugins can be created.

The output of the previous rendering step is run through all converters that match the filename extension of the file being processed.

3. Place output in layouts

If the layout parameter for a page or document is defined, then the contents of the document are rendered within that layout.

How this works might not be immediately obvious, so let's look at an example:

Say you have a Hello World blog post in your _posts directory, which defines layout: post in its front matter. The renderer then goes through the following steps:

  1. Load post.html from your _layouts directory.
  2. Render post.html using Liquid with the content parameter set to the contents of the Hello World blog post.
  3. Check if post.html contains a layout parameter. If so, repeat the process from step one, with the current output as input, instead of the blog post.

5. Cleanup

Your generated site is now ready to be written to disk, but before Jekyll can do that it needs to clean up the destination directory. All the existing files in the destination directory that are not part of the current build process are removed at this stage.

6. Write

For each file that belongs to the site being generated, its contents are written to disk. Static files are simply copied over to their destination path. Directories are created as needed to match the destination path for each file.

7. Print stats

If you run jekyll build with the --profile option, this step will print out a stats table from the Liquid renderer, which lists the rendering times for each file. For example:

Liquid stats

There's more to it

The steps outlined above should provide a basic understanding of how Jekyll works and will hopefully show that a static site generator is not magical. Which means that if something breaks, or doesn't work as you'd expect, you can always go in and find out how it works, or why it's broken. The source code for Jekyll is up on Github for anyone to check out.

There’s a lot more to Jekyll that I haven’t discussed, such as defining custom tags and registering hooks. You can learn more about those features and others on the Jekyll documentation site.