I recently migrated from Octopress to Jekyll after I’ve figured out how to build a vanilla Jekyll project. In this tutorial, you’re going to learn how to start a new project and manage the dependencies correctly.
Why Another Jekyll Tutorial?
Most of the online tutorials don’t address my main concern with learning a new framework.
How do I build a Jekyll site without a built-in theme and dependencies? I want to make everything from scratch.
You’re likely to find this tutorial useful if you fit into the following criteria:
- Comfortable developing a website with only HTML, CSS, and JavaScript.
- Use version control like Git to track and deploy your projects.
- Understand the advantage of using Homebrew to manage packages.
- Prefer to use version managers like rbenv, pipenv, and nvm to avoid running
sudo
when installing Ruby, Python, or Node on your working machine.
Recommended Way to Start a New Jekyll Project
There is a quick way to start a Jekyll site, but we’re going to choose the best way by starting the project with Bundler.
Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed.
With Bundler, you can clone your repository and get the Jekyll site running in minutes. Read more about installing Jekyll with Bundler. Run the command lines below to create a my-jekyll-website
folder and set up a bundle.
mkdir my-jekyll-website
cd my-jekyll-website
bundle init
This is optional, but I prefer to install the bundle in the project subdirectory to avoid permission errors you might get during the gem installation.
bundle config set --local path 'vendor/bundle'
Instead of running bundle add jekyll
to add Jekyll as a dependency for your new project, you want add this line item gem "jekyll", github: "jekyll/jekyll"
to the Gemfile
.1 Replace the content of Gemfile
with the following snippet.
source "https://rubygems.org"
gem "jekyll", github: "jekyll/jekyll"
A Gemfile is a file we create which is used for describing gem dependencies for Ruby programs. A gem is a collection of Ruby code that we can extract into a “collection” which we can call later. — Tosbourn
Install the gems required to run Jekyll with the command below. Wait until the installation process is complete.
bundle install
Run the following command to create a new Jekyll folder called source
that contains your source code. We need to add --blank
option to create a blank Jekyll directory structure to help you understand the purpose of each file and folder.
bundle exec jekyll new source --blank
Move _config.yml
from the source
directory to the root directory by running this command:
mv source/_config.yml .
Here’s the directory structure of your Jekyll project.
my-jekyll-website/
| source/
| vendor/
| _config.yml
| Gemfile
| Gemfile.lock
Execute the following command to build and serve the site locally.
bundle exec jekyll serve
You will see this build warning in the console.
Build Warning: Layout 'default' requested in source/index.md does not exist.
The first line mentioned that the homepage index.md
is using the default
layout that doesn’t exist. This is happening because _config.yml
tried to build the site using the folders from the same directory, which happened to be in the source
folder. We can fix this error by updating config.yml
.
url: "" # the base hostname & protocol for your site, e.g. http://example.com
baseurl: "" # the subpath of your site, e.g. /blog
title: "" # the name of your site, e.g. ACME Corp.
# Set the source folder
source: source
Rebuild the site with this command that tells Jekyll to incrementally update the site whenever it detects change with any of the files.
bundle exec jekyll serve --incremental
You should be able to view the site by visiting http://127.0.0.1:4000/. Congratulations! You’ve learned how to set up and build a Jekyll site.
Development with Jekyll
This is the most exciting and time-consuming step in the whole process: developing your Jekyll website from scratch.
Jekyll is a static site generator, so you can create index.html
in the source directory to build a single-page website.
But, we’re not going to settle with a single page site.
We’re building a site that comes with a blog, a site that can have many posts and pages, and a site where you can experiment with a different type of layouts.
Setting Up Git
Before we start making changes to the project, we want to use Git to track any updates with our files. Go ahead and run git init
to track your project while you’re in the console. You also want to add these lines to .gitignore
file found in the folder.
# Jekyll
.bundle
.jekyll-cache/
.jekyll-metadata
.sass-cache/
_site/
Gemfile.lock
vendor/
You can also add these lines above into your global .gitignore
that can be found in ~/.gitignore
. It’s useful to add them as a global setting if you find yourself developing many Jekyll projects in the future.
Jekyll Directory Structure
Jekyll is the most elegant static site generator I’ve used. It’s the only framework where I only have to deal with HTML, CSS, and Markdown files.
Each folder created in the source directory has a purpose. I recommend that you read the explanation of the directory structure before you continue with this tutorial.
The first concept we need to remember is any post or page in Markdown format can be rendered as a special file only when we’ve added a valid front matter at the beginning of the document. I’ve spent many hours unable to figure out why some pages don’t show up correctly because I forgot about this concept.
Front Matter — it must be the first thing in the file and must take the form of valid YAML set between triple-dashed lines.
Open index.md
inside source
directory to see its content. You will find that layout
variable is set inside the front matter.
---
layout: default
---
It tells Jekyll to use the default.html
found inside _layouts
folder as its parent layout. If you modify anything in default.html
, it will also be rendered for any page that uses default.html
as the layout.
You’re probably wondering how index.md
content shows up in default.html
. Let’s learn more about it by checking out the content:
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>{{ page.title }} - {{ site.title }}</title>
<link rel="stylesheet" href="{{ "/assets/css/main.css" | relative_url }}">
</head>
<body>
{{ content}}
</body>
</html>
The {{ content }}
tag tells Jekyll to load the content of a post or page that uses this layout.
You must remember that _layouts
folder is your building blocks to design a website. The only time you want to modify anything inside this folder is whenever you want to change your site HTML structure. You never have to write the content directly here.
Typically, you want to use default.html
as the first building block for other layouts you’re going to create. Here’s a common way to structure the _default.html
.
<!DOCTYPE html>
<html lang='en'>
{% include head.html %}
<body>
<div class="container">
{% include header.html %}
{{ content }}
{% include sidebar.html %}
{% include footer.html %}
</div>
</body>
</html>
Notice that we’re using {% include <filename.ext> %}
tag several times. It tells Jekyll to include the corresponding files that are found inside _includes
folder.
You can treat anything inside _includes
folder as components for you to use in your layout or post. You can also create more folders there to further organize your components.
Here is what I have in the folder when this post is published. You’re going to create additional components as you build a more complex layout, or by adding various sections on your site.
_includes/
| custom/
| extra.html
| opengraph.html
| twitter_card.html
| footer.html
| head.html
| header.html
| sidebar.html
Organize Pages with Collections
We’re halfway into this tutorial, but we haven’t discussed anything about the posts
folder.
Let’s take a break.
Good?
You want to make your website looks alive with many pages. You don’t want to have a homepage full of blog posts without other pages to feature a different type of content.
There are two ways to create a new page.
First, you can add more pages by creating a new folder named after the permalink you want to use, and put an index.md
or index.html
in the folder. For example, here is how the source directory looks like if you want to create an about
page with this method.
source/
| _data/
| _drafts/
| _includes/
| _layouts/
| _posts/
| _sass/
| about/
| index.html
| assets/
| posts/
| index.html
This is the easiest way to create a new page, but you may find that your source
folder cluttered with folders if you’re creating many pages.
There is a better way to create pages in Jekyll. We’re going to use collections.
Start by creating a new folder called _pages
on the same directory level as others. Create a new file called about.html
and put it inside _pages
folder.
---
layout: default
title: About
permalink: /about/
---
Tell people about yourself.
You will find a new variable called permalink
inside the front matter. You can change it into permalink: /me/
if you want the about page accessible from visiting http://127.0.0.1:4000/me/
The pages won’t be accessible yet because there is one more step left. We’ve to set up the collections by adding these lines to _config.yml
.
# Jekyll Collections
collections:
pages:
output: true # Set to render files in _pages folder
permalink: /:title/ # Set the format of the permalink by default
Visit http://127.0.0.1:4000/about/ again should load the page.
With the _pages
folder ready, you can add more pages by creating html
or markdown
files in the folder. The structure remains organized even after we’ve added a contact and portfolio page.
source/
| _data/
| _drafts/
| _includes/
| _layouts/
| _posts/
| _pages/
| about.html
| contact.html
| portfolio.html
| _sass/
| assets/
| posts/
| index.html
Pagination and Iteration with Posts
If you’ve published any posts from your previous setup, you can copy markdown files into the new project posts
folder. We’re going to use actual content to start building the posts page.
Pagination is the most important feature on the posts page because we want to avoid displaying all the posts on a single page. Instead of using jekyll-paginate
recommended by Jekyll, we’re going to use jekyll-paginate-v2
to create a paginated posts page.
Open Gemfile
and update its content into:
source 'https://rubygems.org'
gem "jekyll", github: "jekyll/jekyll"
group :jekyll_plugins do
gem 'jekyll-paginate-v2'
end
There are three ways to install plugins, but I recommend using Gemfile
because we started with Bundler to manage the dependencies.
To enable pagination, you need to configure the pagination settings in _config.yml
:
url: "" # the base hostname & protocol for your site, e.g. http://example.com
baseurl: "" # the subpath of your site, e.g. /blog
title: "" # the name of your site, e.g. ACME Corp.
source: source
# Pagination
pagination:
enabled: true # must be set to true
per_page: 10 # number of posts displayed per page
permalink: '/page/:num/' # the pagination format
sort_reverse: true # show the latest post first
# Collections
collections:
pages:
output: true # Set to render files in _pages folder
permalink: /:title/ # Set the format of the permalink by default
You can now access paginator
liquid object to iterate every post as long you’ve enabled it in the front matter.
Depending on where you want to display your latest posts, you will have a different directory structure. If you’re going to show the latest posts and pagination on the homepage, you can rename index.md
into index.html
and update the content with this:
---
layout: default
pagination:
enabled: true # Enable pagination on this page
---
<!-- This loops through the paginated posts -->
{% for post in paginator.posts %}
<h1><a href="{{ post.url }}">{{ post.title }}</a></h1>
<span class="date">{{ post.date }}</span>
<div class="content">
{{ post.content }}
</div>
{% endfor %}
<!-- Pagination links -->
<div class="pagination">
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path }}" class="previous">
Previous
</a>
{% endif %}
{% if paginator.next_page %}
<a href="{{ paginator.next_page_path }}" class="next">Next</a>
{% endif %}
</div>
Your site now supports the basic features of a blogging platform. You can create more pages by creating files in _pages
folder. Here are more tips and resources to help you get started developing your site:
- Learn about Liquid filters and tags because it’s the primary language to code the site.
- Learn about Jekyll filters that are useful to format date, sort data, and manage arrays.
- Specifying Jekyll environment value to separate content for development and production.
- Learn more about
_data
folder and how you can use it to populate repetitive data.
Deployment to GitHub Pages
Check out how to configure custom domain on GitHub Pages if you haven’t done so. If you’ve already published your site on GitHub, you want to clone the repository to your desktop by using this command:
git clone <your_github_repo_url> ~/Desktop/_site
Replace your Jekyll _site
folder with the cloned _site
folder from GitHub. You should see that the folder contains files like CNAME
and .git
. To prevent Jekyll from removing them whenever we build the site, we need to exclude them by adding this line into _config.yml
keep_files: [CNAME, .git]
To build for the production, you can enter this command:
JEKYLL_ENV=production bundle exec jekyll build
It will override the content of _site
folder with the new one. Once you’re satisfied with your site, you can push the commit back to GitHub to make the new site live.
Conclusion
Jekyll is the first framework I can use to swiftly build and deploy a static site from scratch. It provides me with the environment to practice the fundamental of building a semantic and responsive site with HTML/CSS.
I know there are people like me who prefer to keep the website development process simple: a plain old HTML, CSS, and maybe some JavaScript to spice up the site.
There is a bug with the version of Jekyll that is hosted on RubyGems.org. That’s why you want to use the latest stable release from the repository directly. ↩