15 Feb 2025
Jekyll Guide
Jekyll – static site generator
- it has to build the site before we can view it
Gemfile – for dependency management
Bundler – manages Ruby gem dependencies
1. Setup
1.1. Installation
- Install
Rubyon your machine, if not installedbrew install ruby ruby -v - Navigate to the git repo/branch and the docs directory from where you want to publish the site
cd docs - Install Jekyll from the terminal
gem install jekyll bundler - Create a new
Gemfileto list project’s dependenciesbundle init - Edit the
Gemfilein a text editor and add jekyll as a dependency:gem "jekyll"source "https://rubygems.org" gem "jekyll"
Run bundle to install jekyll for project
bundle
- You can now prefix all jekyll commands listed in this guide
with
bundle execto make sure you use the jekyll version defined in theGemfile.
1.2. Create a site
Create index.html or index.md in docs root directory
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Home</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
# Home
## Hello World!
1.3. Build
- Run either of the following commands to build the site:
-
jekyll build- Builds the site and outputs a static site to a directory called _siteOR
-
jekyll serve- Does jekyll build and runs it on a local web server at http://localhost:4000, rebuilding the site any time you make a change.
-
- When you’re developing a site, use
jekyll serve.
- To force the browser to refresh with every change
jekyll serve --livereload -
If there’s a conflict, or you’d like Jekyll to serve your development site at a different URL,
use the
--hostand--portarguments - The version of the site that jekyll serve builds in
_siteis not suited for deployment.- Links and asset URLs in sites created with jekyll serve will use https://localhost:4000 or
- the value set with command-line configuration, instead of the values set in your site’s configuration file.
2. Liquid
Templating language
Use Liquid
To make Jekyll process your changes, add front matter to the top of the page
---
# front matter tells Jekyll to process Liquid
---
Liquid has 3 main components
2.1. Objects
output predefined variables as content on a page
{{ page.title }} // displays the page.title variable
2.2. Tags
define the logic and control flow for templates
{% if page.show_sidebar %}
<div class="sidebar">
sidebar content
</div>
{% endif %}
// displays the sidebar if the value of the show_sidebar page variable is true.
2.3. Filters
change the output of a Liquid object
They are used within an output and are separated by a |
{{ "hi" | capitalize }} // displays Hi instead of hi
3. Front Matter
Front matter must be included on the page for Jekyll to process any Liquid tags on it
Front matter: a snippet of YAML placed between two triple-dashed lines at the start of a file
---
my_number: 5
---
- front matter can be used to set variables for the page
{{ page.my_number }}
E.g:
---
title: Home
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title>
</head>
<body>
<h1>{{ "Hello World!" | downcase }}</h1>
</body>
</html>
---
title: Home
---
# {{ page.title }}
## {{ "Hello World!" | downcase }}
4. Layouts
Jekyll supports Markdown in addition to HTML when building pages.
4.1. Creating a Layout
Layouts are templates that can be used by any page in your site and wrap around page content.
They are stored in a directory called _layouts.
Create the _layouts directory in your site’s root folder and create a new default.html file.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title> <!-- browser tab title -->
</head>
<body>
{{ content }}
</body>
</html>
Compared to html files with content, layout...
- has no front matter
- the content of the page is replaced by a
contentvariable.- content: a special variable that returns the rendered content of the page on which it’s called.
4.2. Use layouts
Add the layout variable in the front matter of about.md/about.html to use new layout in the About page:
---
layout: default
title: About
---
<p>{{ "about content" | upcase }}</p>
---
layout: default
title: About
---
{{ "about content" | upcase }}
Open http://localhost:4000/about.html in your browser
5. Includes
to navigate between pages
Navigation should be on every page so adding it to your layout is the correct place to do this.
5.1. Include tag
Allows you to include content from another file stored in an _includes folder.
- useful for having a single source for source code that repeats around the site or for improving the readability.
5.2. Include usage
Create a file for the navigation at _includes/navigation.html
<nav>
<a href="/">Home</a>
<a href="/about.html">About</a>
</nav>
Use the include tag to add the navigation to _layouts/default.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title>
</head>
<body>
{% include navigation.html %}
{{ content }}
</body>
</html>
Open http://localhost:4000 in your browser and try switching between the pages.
5.3. Current page highlighting
_includes/navigation.html needs to know the URL of the page it’s inserted into so it can add styling.
Using page.url variable you can check if each link is the current page and color it red if true:
<nav>
<a href="/" {% if page.url == "/" %}style="color: red;"{% endif %}>
Home
</a>
<a href="/about.html" {% if page.url == "/about.html" %}style="color: red;"{% endif %}>
About
</a>
</nav>
Take a look at http://localhost:4000
6. Data Files
Jekyll supports loading data from YAML, JSON, and CSV files located in a _data directory.
6.1. Data file usage
Use YAML to store an array of navigation items each with a name and link.
Create a data file for the navigation at _data/navigation.yml
- name: Home
link: /
- name: About
link: /about.html
Jekyll makes this data file available to you at site.data.navigation.
Instead of outputting each link in _includes/navigation.html, now you can iterate over the data file instead:
<nav>
{% for item in site.data.navigation %}
<a href="{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}>
{{ item.name }}
</a>
{% endfor %}
</nav>
7. Assets
Place CSS, JS, images and other assets in your site folder and they’ll copy across to the built site.
assets structure:
.
├── assets
│ ├── css
│ ├── images
│ └── js
...
7.1. Sass
Sass is a fantastic extension to CSS baked right into Jekyll
- Can use standard CSS files for styling.
location: _sass folder directly under the root
- Inlining the styles used in
_includes/navigation.htmlis not a best practice.
Sass Usage
- stylesheet file:
assets/css/styles.scss--- --- // Empty front matter at the top: tells Jekyll it needs to process the file. @import "main"; // @import "main": tells Sass to look for a file called main.scss in the sass directory (_sass/) by default - sass file:
_sass/main.scss// Create a class named "current" in order to color the current link green. .current { color: green; } -
reference the stylesheet in layout:
_layouts/default.htmlby adding the stylesheet to the<head><!doctype html> <html> <head> <meta charset="utf-8"> <title>{{ page.title }}</title> <link rel="stylesheet" href="/assets/css/styles.css"> </head> <body> {% include navigation.html %} {{ content }} </body> </html>- The
styles.cssreferenced here is generated by Jekyll from thestyles.scsscreated inassets/css/.
- The
-
style usage in html files by referring to the class:
navigation.html<nav> {% for item in site.data.navigation %} <a href="{{ item.link }}" {% if page.url == item.link %}class="current"{% endif %}>{{ item.name }}</a> {% endfor %} </nav> - Load up http://localhost:4000
8. Blogging
Blog without a database.
In Jekyll style, blogging is powered by text files only.
8.1. Posts
folder for blog posts: _posts
Filename format for posts: [PUBLISH_DATE_YYYY:MM:DD]-[TITLE].[EXTENSION]
-
an example post:
_posts/2025-02-10-blog1-file-name.md--- layout: post author: jill title: Blog1 Title --- # Welcome to Jekyll Posts This is an example of a blog post.- author is a custom variable
-
post layout:
_layouts/post.html:--- layout: default --- <h1>{{ page.title }}</h1> <p>{{ page.date | date_to_string }} - {{ page.author }}</p> {{ content }}- This is an example of layout inheritance.
date_to_stringfilter formats a date into a nicer format.
8.2. Listing posts
Jekyll makes posts available at site.posts.
-
blogs.htmlin your root (/blogs.html) - the page which lists all the posts--- layout: default title: Blog --- <h1>Latest Posts</h1> <ul> {% for post in site.posts %} <li> <h2><a href="{{ post.url }}">{{ post.title }}</a></h2> {{ post.excerpt }} </li> {% endfor %} </ul>post.url: automatically set by Jekyll to the output path of the postpost.title: pulled from the post filename and can be overridden by setting title in front matterpost.excerpt: the first paragraph of content by default
-
_data/navigation.ymlwith an entry for the blogs page- name: Home link: / - name: About link: /about.html - name: Blog link: /blogs.html -
Add few more posts and open http://localhost:4000
9. Front matter defaults
Configure
- all posts to automatically have the post layout,
- authors to have author layout, and
- everything else to use the default layout.
- Using front matter defaults in
_config.ymlset a scope of what the default applies to,- then the default front matter you’d like.
collections: authors: output: true defaults: - scope: path: "" type: "authors" values: layout: "author" - scope: path: "" type: "posts" values: layout: "post" - scope: path: "" values: layout: "default"
- then the default front matter you’d like.
- Now you can remove layout from the front matter of all pages and posts.
- Note that any time you update
_config.ymlyou’ll need to restart Jekyll for the changes to take effect.
10. Collections
Collections are similar to posts except the content doesn’t have to be grouped by date.
10.1. Configuration
default jekyll configuration file: _config.yml @ root directory for gh pages
collections:
authors:
- To (re)load the configuration, restart the jekyll server.
- Press
Ctrl+Cin your terminal to stop the server, and - then
jekyll serveto restart it.
- Press
10.2. Usage Example
Documents (the items in a collection) live in a folder in the root of the site named _<COLLECTION_NAME>.
Jekyll makes the collection available at site.<COLLECTION_NAME>.
By default, collections do not output a page for documents.
authors collection
Add authors
- a document for each author:
_authors/jill.md--- short_name: jill name: Jill Smith position: Chief Editor --- Jill is an avid fruit grower based in the south of France.
-
Jekyll makes the collection available at
site.authors. -
Authors page - a page which lists all the authors on the site
--- layout: default title: Authors --- <h1>Authors</h1> <ul> {% for author in site.authors %} <li> <h2>{{ author.name }}</h2> <h3>{{ author.position }}</h3> <p>{{ author.content | markdownify }}</p> </li> {% endfor %} </ul>- Since the content is markdown, you need to run it through the
markdownifyfilter. - This happens automatically when outputting using
{{ content }}in a layout.
- Since the content is markdown, you need to run it through the
- add an entry for the authors page:
_data/navigation.yml- name: Home link: / - name: About link: /about.html - name: Blog link: /blogs.html - name: Staff link: /authors.html
Output pages of authors so that each author has their own page
-
By default, collections do not output a page for documents.
- configuration: setting
output: truein the author collection configuration in_config.ymlcollections: authors: output: true - Restart the jekyll server for the configuration changes to take effect.
-
Link to the output page using
author.urlinauthors.htmlpage--- layout: default title: Authors --- <h1>Authors</h1> <ul> {% for author in site.authors %} <li> <h2><a href="{{ author.url }}">{{ author.name }}</a></h2> <h3>{{ author.position }}</h3> <p>{{ author.content | markdownify }}</p> </li> {% endfor %} </ul> - Author pages layout:
_layouts/author.html--- layout: default --- <h1>{{ page.name }}</h1> <h2>{{ page.position }}</h2> {{ content }}
List author’s posts
List the posts an author has published on their page.
- Iterate over this filtered list in
_layouts/author.htmlto output the author’s posts:--- layout: default --- <h1>{{ page.name }}</h1> <h2>{{ page.position }}</h2> {{ content }} <h2>Posts</h2> <ul> <!-- loops through `site.posts` and keeps only those posts where the `author` front matter in each post matches the value of `page.short_name` --> {% assign filtered_posts = site.posts | where: 'author', page.short_name %} {% for post in filtered_posts %} <li><a href="{{ post.url }}">{{ post.title }}</a></li> {% endfor %} </ul>
Link to authors page from the post
The posts have a reference to the author so let’s link it to the author’s page.
- in
_layouts/post.html:--- layout: default --- <h1>{{ page.title }}</h1> <p> {{ page.date | date_to_string }} <!-- Link to author's page--> {% assign author = site.authors | where: 'short_name', page.author | first %} {% if author %} - <a href="{{ author.url }}">{{ author.name }}</a> {% endif %} </p> {{ content }} - Open up http://localhost:4000