Jekyll is a static website generator—it helps making websites more efficient by reducing duplication.

Why use Jekyll?

The main reasons for using Jekyll are efficiency, it eliminates lots of copying and pasting in our websites amoung other things:

  • Makes use of ☛ Markdown to reduce the overhead of HTML,
  • Allows sharing of HTML code between pages,
  • Has the ability to automatically write repeated HTML,
  • Can be hosted on GitHub using GitHub pages.

Jekyll isn’t the only way to get these features, you could use other static site generators or even other languages like PHP, Python, or Ruby.

Jekyll Installation

Jekyll is a command line tool, it requires the Apple command line tools to be installed.

☛ Check out the Jekyll installation guide.


Setting up Jekyll

To set up a Jekyll project you must add a file named _config.yml. Put this directly in your folder, in the same place as your index.html file.

Here’s a sample config file:

permalink: pretty
baseurl: /your-folder-on-github

After you have your config file, you can run Jekyll in the Terminal.


Running Jekyll

Jekyll must be running in the Terminal when you are actively developing a project. Every time you save a file, Jekyll does its stuff to the file and copies it to the _site folder, where the final version of the website is found.

☛ Check out the Jekyll terminal guide.


The _site folder

When Jekyll is running and you save one of your files it will be copied to the _site folder. The _site folder is completely temporary, but is also the final, compiled version of your website.

If you delete the _site folder, when you save a file again it will immediately come back.

Never have the _site folder on GitHub

Since the _site folder is temporary it should never be committed to GitHub.

Git has a method for ignoring files, so that they can never be accidentally committed.

If you create a new file named .gitignore, in the same location as your index.html Git will look here for what files and folders to hide.

Inside the .gitignore file you can write the folder you want to hide, like this:


Commit it into your repository and Git will safely ignore the _site folder from now on.

☛ Read more about .gitignore.


Jekyll has a feature named layouts that allows designers to put the header and footer in a single file and have them shared between every page on the site—kind of like master pages in InDesign.

This is probably one of the most compelling reasons to use Jekyll.

First open you index.html file and look for stuff that will be common to every page, like headers, footers, DOCTYPE, etc.

Cut that stuff and put it into a new file: default.html. It doesn’t have to be called “default”, that’s just a convention.

Save this file into a folder named _layouts.

_layouts/        ⬅︎ The layouts folder
  └ default.html

The file might look something like this:

<!DOCTYPE html>
<html lang="en-ca">
  <meta charset="utf-8">



The {{content}} placeholder is where Jekyll will place the content from the index.html.

Then, inside your index.html, all you’ll have left is this:


Jekyll doesn’t magically know what layout to use, so at the top of our index.html file we need to include a little ☛ YAML to Jekyll where to find the layout.

layout: default


And that’s it. If you want to make a second page all you have to do is include the YAML front matter and the layout will automatically be applied to your new page.

Nested layouts

With Jekyll we can even have layouts that fit into other layouts. An example of when this is helpful is if you have a sub section of your website with a common navigation, but it still has the websites main navigation.

In our _layouts folder we just need to make two files, like this:

  └ default.html
  └ sub-section.html

Our sub-section page, would point to the sub-section layout, like this:


layout: sub-section

Then, the sub-section.html layout file would point to default, like this:


layout: default

Passing information from pages to layouts

Using the YAML at the top of pages we can send information up to the layout to populate specific fields:


layout: default
title: Amazing dinosaurs!
bodyClass: dinos-page

Then, inside our default.html layout we can use that information wherever we want:


<!DOCTYPE html>
<html lang="en-ca">
  <meta charset="utf-8">
<body class="{{page.bodyClass}}">

We have variables in two places: {{page.title}} and {{page.bodyClass}} that are basically placeholders, waiting for the information from the page.

Notice how the name of the variables is prepended by the page., indicating that the information is coming from the top of the page itself.


Linking to other pages

If you’re using the permalink: pretty setting in you _config.yml—which I suggest doing—then linking to pages is slightly different from normal.

Usually we link directly to the HTML file, with the HTML extension, but Jekyll is actually making a folder for each page in order to hide the HTML extension from the URL. So, we only need to link to the folder.

If we have a folder setup like this:

  └ default.html

Then, the navigation inside our layout would look like this:

    <li><a href="/">Home</a></li>
    <li><a href="/about/">About</a></li>
    <li><a href="/contact/">Contact</a></li>

Notice how we’re haven’t included the .html extension in our URLs.

Base URLs

If you’re using GitHub as a host then your project is within a folder, when looking at it live on GitHub’s URL. But on your local computer there is no folder.

On your local computer, the URL might look like this:

But on GitHub, it will look like this:

Notice the extra folder between the domain and the name of the page—this is the base URL.

We even add the base URL into our _config.yml file when setting it up. But we still need to include it in our HTML for the navigation to work consistently across all locations.

    <li><a href="{{site.baseurl}}/">Home</a></li>
    <li><a href="{{site.baseurl}}/plant-eaters/">Plant eaters</a></li>
    <li><a href="{{site.baseurl}}/meat-eaters/">Meat eaters</a></li>

Notice the addition of {{site.baseurl}}, this allows our local computer to not have the extra folder in the URL, but the remote GitHub host computer to include the extra folder.

Highlighting navigation

It’s good practice to highlight the navigation on the website to help your users understand where they are in the structure of the site.

Jekyll can do this with some if-statements inside our navigation’s <a> tags. The if-statements check what page is current then only add a class to the appropriate <a> tag.

<a href="{{site.baseurl}}/plant-eaters/" {% if page.url == '/plant-eaters/' %} class="current" {% endif %}>Plant eaters</a>

The if-statement is wrapped around the class="…" attribute, therefore the class will only shown on the <a> tag when the page is active. It can be styled in CSS just like normal.

Linking images

Linking to images isn’t really that different from regular, but the addition of {{site.baseurl}} makes them work more reliably across hosts.

<img src="{{site.baseurl}}/images/trex.jpg" alt="">

Adding CSS

When making Jekyll websites, CSS works exactly the same as normal, because Jekyll is just outputting straight-up HTML—so, from the CSS’s perspective, Jekyll doesn’t actually exist.

But, it’s a good idea to link the CSS file with the {{site.baseurl}} for reliably.

<link href="{{site.baseurl}}/css/main.css" rel="stylesheet">

Data files

Data files are a great way to separate the content from the its presentation HTML. Data files are mostly written in YAML, but other languages can also be used.

First create a new folder in your Jekyll website named _data, put it in the same location as the index.html and the _layouts folder.

_data/       ⬅︎ The data folder

Then inside that folder make your data file, named with the .yml extension. Here’s an example data file:


- name: Tyrannosaurus
  diet: Meat
  size: Big
- name: Stegosaurus
  diet: Plants
  size: Medium
- name: Velociraptor
  diet: Meat
  size: Small

After we have the data file created we can use it in our website. The great thing about doing it this way is that we don’t have to copy and paste our HTML, Jekyll will do the work for us.

{% for dino in %}
{% endfor %}

Using a for loop we’re looping over every entry in the data file. The is how we access our data file—the .dinos part is exactly the name of the file.

Because there are three dinosaurs in the data file, Jekyll will automatically output the above HTML three times: once for each dinosaur.



Includes are reusable pieces of HTML, akin to symbols in Illustrator. We can create one file with some HTML in it, then use that file in multiple locations. If we update the single file all uses will automatically get updated.

To create an include we first need to make the specialized folder: _includes. Put it in the same place as _layouts and index.html.

_includes/    ⬅︎ The includes folder

Then inside that folder make your HTML file, named with the .html extension. Here’s an example:


<a class="btn" href="/go/">Go!</a>

In any of our Jekyll pages, we can use the include file to output whatever HTML is inside it:


layout: default
{% include button.html %}
{% include button.html %} 

Notice that we’re using the include tag to bring in the HTML twice into the file.

Include parameters

With the above (very simple) example, there’s a small issue: both buttons would say “Go!” and point to the same page. We can overcome this limitation by using include parameters.

First, we change our include to have place holders in it, like this:


<a class="btn" href="{{include.url}}">{{include.text}}</a>

Notice that we changed the variable sections to place holders {{include.url}} and {{include.text}}. The include. part indicates the information is coming from an include. The part that comes after the dot is just made up.

Now, in our HTML we can adjust the includes to have the variable information:


layout: default
{% include button.html url="/prev/" title="Previous" %}
{% include button.html url="/next/" title="Next" %}

My example above is extremely simple, and probably doesn’t make sense to do for a button (because the include code is practically the same length as the original HTML), but hopefully it communicates the powerful idea behind includes.

Relative includes

We don’t have to put our includes into the _includes folder if we use the include_relative function instead. This function will look inside the same folder as the current file for the include to insert. It works really great for concatenating CSS files together.


{% include_relative header.css %}
{% include_relative footer.css %}
{% include_relative nav.css %}
{% include_relative cards.css %}

This could be a main.css file that includes all the other smaller files to make our website more performant.


Jekyll supports posts, like blog posts, that can be used as an ordered type of content.

Posts must be named very strictly and stored inside the _posts folder:


The post’s file name must begin with a properly formatted date, in the format: YYYY-MM-DD.

To display a list of posts in your website, you can use Jekyll’s loop:

  {% for post in site.posts %}
      <a href="{{site.baseurl}}{{post.url}}">{{post.title}}</a>
  {% endfor %}



Collections are a mechanism in Jekyll that allow us to have a grouping of documents. The grouping can either act like datafiles or can act like pages and posts. In fact, posts are just built-in collections.

Set up a collection in _config.yml

The first thing we need to do is create a collection in _config.yml, telling Jekyll how to handle it.

    output: true
    permalink: /comets/:path/
  • The collection entry tells Jekyll we’re including new collections.
  • The comets entry is the name of our collection—we completely make it up.
  • The output entry is optional, here we’re telling Jekyll to turn the collection files into pages we can navigate to.
    Without output the collection wouldn’t be navigable but could be pulled onto other pages like datafiles.
  • The permalink entry tells Jekyll how to generate the URL for the collection pages.

Outputting collection documents

In a page on our website we can loop through all the documents of a collection to generate some HTML.

{% for comet in site.comets %}
  {{comet.content | markdownify}}
{% endfor %}

Collection pages

If the collection is set to output we can add layouts and everything else we’re used to doing with regular pages.

Video list

  1. Jekyll: Installation
  2. Jekyll: Installation on older MacOS
  3. Jekyll: Setting up on GitHub
  4. Jekyll: Setting up a Jekyll project
  5. Jekyll: Starting and stopping
  6. Jekyll: The _site folder
  7. Jekyll: Layouts
  8. Jekyll: Creating navigation
  9. Jekyll: Site baseurl
  10. Jekyll: Includes
  11. Jekyll: Adding CSS
  12. Jekyll: Highlighting navigation
  13. Jekyll: Include parameters
  14. Jekyll: Looping over posts
  15. Jekyll: Post categories
  16. Jekyll: Limiting the post loop
  17. Jekyll: Nested layouts
  18. Jekyll: Highlighting navigation on multiple pages
  19. Jekyll: Nested layouts for a specific content type
  20. Jekyll: Using posts for ordered content
  21. Jekyll: Data files
  22. Jekyll: Putting pages inside folders
  23. Jekyll: Looping over pages
  24. Jekyll: Includes with page loops
  25. Jekyll: Page loops and images
  26. Jekyll: Advanced nested layouts for specific content types
  27. Jekyll: Page sub-folders
  28. Jekyll: Generating page navigation
  29. Jekyll: Changing background images
  30. Jekyll: Many CSS files
  31. Jekyll: Relative includes
  32. Jekyll: Unique titles for every page
  33. Jekyll: Meta description
  34. Jekyll: Automatic sitemap.xml
  35. Jekyll: Pretty URLs
  36. Jekyll: Creating a collection
  37. Jekyll: Extra collection info
  38. Jekyll: Collection index list
  39. Jekyll: Collection in sitemap.xml