The Ultimate Guide to Static Websites with Jekyll
code

A few months ago I said goodbye to WordPress and switched my site over to Anchor CMS. It was a very refreshing experience but after reading about yet another round of security warnings and realising that I was writing all my blog posts in a text editor anyways, I knew it was time for a change. I set up Jekyll, converted my theme and went 100% static.

Why Static?

Most personal blogs don't actually need a heavyweight content management system like WordPress. It's funny to call WordPress a "heavyweight CMS" when it used to be the only decent CMS that wasn't a bulky monster. But here's the thing: People's needs and expectations have changed. Nowadays, a popupar CMS needs to cover every possible need a user might have and make it as easy as possible for everyone to use it.

The advantages of content management systems are all great for large-scale digital publishing: a straightforward interface that even your mum can use, an easy integration of third-party services and seamless database communication. But realistically, my blog doesn't need a back-end that makes publishing easy for everyone. The only person writing, publishing and editing here is me so it needs a workflow that makes it easy for me.

Smashing Magazine's recent article on static website generators made a really good point: In a way, Dreamweaver and FrontPage (or even Adobe GoLive – oh, the memories!) were the original static website generators. Using a WYSIWYG interface, they let you put together websites with reusable components and when you hit save, it created a static page which you could then upload to your web server.

With the maturation of browsers, many features that used to require dynamic code running on a server can be moved entirely to the client. Want comments on your website? Add Disqus, Isso or Facebook comments. Want social integration? Add Twitter or Facebook’s JavaScript widget to your website. Want real-time data updating live on your website? Add a squirt of Firebase.

Generating your entire website on the server with all your data stored there doesn't only make your website incredibly vulnerable to attacks and various other problems, it can also make it incredibly slow. Every pageview most likely requires a server and database request, which is why every larger website probably uses some form of caching to deliver their content to the users. That basically means: Dynamic content is temporarily turned into static content in order to improve performance and stability.

So why not go static in the first place?

Build the Blog System You've Always Wanted

WordPress and similar blogging software got us very used to just accepting what's there. You log in, hit "New Post", fill in the fields and you're done. Custom fields and custom templates are totally possible, but it's very easy to find yourself simply tweaking what's already there (with varying results) instead of actually creating what you really need.

It's actually pretty inspiring and fun to start this whole process all over again from scratch and ask youself the most basic questions:

  • What information do I want to store with each blog post?
  • How do I want to use and display those pieces of information?
  • What settings or logic do I want to implement?
  • How do I want to organise my files?

For example, here's what it says at the top of this blog post:

title: "The ultimate guide to static websites with Jekyll"
subhead: "Going static for beginners – with free template!"
categories: [ 'web', 'code', 'tutorial' ]
layout: post

image: "jekyll.jpg"
color: "#CD1E23"
highlight_code: true

For each blog post, I set the layout to post so it uses my blog post template. I also store a title and a subhead, a list of categories, the name of the title image, a custom colour and a bunch of other random settings. For example, if I set highlight_code to true, it includes the syntax highlighter and displays all code highlighted. There's also readmore which automatically displays a "Read more" button at the bottom of a blog post linking to whichever URL I add. Sure, there might be best practices, but yes, you can totally come up with all of those things yourself!

The Setup

If you're familiar with the command line and/or already have Ruby (and Ruby Gems) installed, simply skip this part and go ahead and install the Jekyll Gem. If not, don't worry, it's easy. The idea of building your blog and doing this in the Terminal might seem weird at first, but you'll mostly be using one simple command only.

  1. Install version 2.0 of the programming language Ruby. If you use OSX Mavericks or a newer version, it should already be there. To check, type in ruby -v. Otherwise, there are plenty of tools to help you install it. If you're on a Mac, make sure that you also have the Xcode Command Line Tools installed.
  2. Install the Jekyll Gem. Ruby comes with RubyGems, a package manager that lets you install plugins and libraries easily. To install, type in gem install jekyll. If you get an error saying you don't have the right permissions, use sudo gem install jekyll and type in your admin password. That's it!

How It Works

To get a feeling for the file structure, it's important to understand how Jekyll works: Every time you run jekyll build in your website folder, it goes through all the files and folders and compiles your website into _site. You end up with all your source files and a perfect, ready-to-upload static copy of your website in your _site folder.

  • Posts in the _posts folder all need to have names like 2015-11-01-your-post-title.html (or .md if you prefer to write in Markdown)
  • Files and folders without an underscore (_) are simply copied over – this is great for images, fonts, stylsheets and scripts
  • Drafts in the _drafts folder are not compiled by default, but you can optionally preview/build your site with drafts
  • Pages can be saved as files in your main folder (for example, about.html) or as an index in their own folder (for example, about/index.html)
  • .sass, .scss and .coffee files are compiled automatically in the same location
  • Settings and all basic configuration, like the page title, the site URL, the permalink structure or the time zone, live in a file called _config.yml

Example structure

Here's an example very similar to the structure of this blog, ines.io:

├── _config.yml
├── _drafts
|   └── this-is-a-draft-post.html
├── _includes
|   ├── footer.html
|   ├── head.html
|   ├── header.html
|   └── share-buttons.html
├── _layouts
|   ├── default.html
|   ├── page.html
|   └── post.html
├── _plugins
|   └── rssgenerator.rb
├── _posts
|   ├── 2015-11-01-this-is-a-blog-post.html
|   └── 2015-11-02-this-is-another-blog-post.html
├── _site
├── assets
|   ├── css
|   ├── fonts
|   ├── img
|   └── js
├── about.html
└── index.html

Creating Your Own Template

The Jekyll Wiki provides a very long list of websites using Jekyll to get inspired (all open source, of course!) but ultimately, it's up to you how you decide to structure your site – which can be super overwhemlming.

To get you started, I've put together a basic skeleton template for a personal blog. It includes all the required files, a nice and clean structure, examples for all the cool stuff you can do (layouts, includes, custom logic) and descriptions here and there to help you understand what all those files are about. I only added the most basic formatting so the rest is up to you.

Screenshot of skeleton blog template

View Skeleton Theme on GitHub

This template is already complete and ready to test. To give it a try, open the Terminal, navigate to your website folder (cd websites/my-cool-website), then run jekyll serve. Wait for it to complete and check out the _site folder. Jekyll simulates a local server to preview your site at http://localhost:4000. To stop the server, press CTRL + C.

The configuration

The _config.yml file contains all global information about your site. Here's an example from my skeleton theme:

name: My Blog
description: This is my blog where I post stuff
author: Me
permalink: /blog/:title
timezone: Europe/Berlin

email: me@domain.com
twitter: mytwitteraccount

# This is the default that's used to preview.
# Change this to your website's URL when you publish your site.
url: http://localhost:4000

The url is set to your localhost, the location Jekyll uses to preview your site. Once you're done and the site is ready to go live, you can replace it with your domain and build your site.

The permalink setting will create links like domain.com/blog/this-is-a-post. All global variables above can be used in our theme. Writing {{site.name}} for example will return "My Blog" anywhere in our templates. If you want to display a link to your Twitter account somewhere, you could do this:

<a href="https://twitter.com/{{site.twitter}}">Follow me on Twitter!</a>

The templating language

Jekyll uses the templating engine Liquid which comes with easy to write and easy to read template tags. No more messy PHP or template tags you don't actually understand. Let's look at the file post.html in the _layouts folder. It contains the basic template for a blog post:

---
layout: default
---

<article class="single">
<header>
    <+h(2)>{{page.title}}</+h(2)>

    {% if page.subhead %}
      <+h(3) class="subhead">{{page.subhead}}</+h(3)>
    {% endif %}

    <div class="meta">by {{site.author}} on {{ page.date | date: "%B %d, %Y" }}</div>

    {% if page.image %}
    <div class="image">
      <img src="{{site.url}}/assets/img/{{page.image}}" />
    </div>
    {% endif %}
</header>

<div class="content">
  {{content}}

  {% if page.readmore %}
    <a href="{{page.readmore}}" class="readmore">Read more</a>
  {% endif %}

  {% include share-buttons.html %}
</div>
</article>

The Front Matter block at the beginning tells Jekyll to use our default page template default.html in the same folder for the page and insert everything in this file as the content. It inserts the page title as a headline and – only if it exists – the subhead. It also inserts the author name and formats the date as "December 14, 2015" (for more options, check out this blog post). Once you build the site, all the main blog content will be inserted in place of {{content}}.

If an image or a "read more" link exists, it's displayed and after the main content, it includes an external file, share-buttons.html. All content elements that you want to reuse – like share buttons, but also the header, footer or even a specific teaser used for certain posts – should be saved as individual files in _includes.

The loop

Here's an excerpt from our index.html that contains a loop to display all posts:

{% for post in site.posts %}
<article>

  {% if post.image %}
  <div class="image">
    <a href="{{site.url}}{{post.url}}">
      <img src="{{site.url}}/assets/img/{{post.image}}" />
    </a>
  </div>
  {% endif %}

  <+h(2)><a href="{{site.url}}{{post.url}}">{{post.title}}</a></+h(2)>

  <div class="excerpt">
    {{post.excerpt | strip_html | strip_newlines}}
  </div>

</article>
{% endfor %}

{{post.excerpt | strip_html | strip_newlines}} displays an excerpt from the post and adds two filters: it removes all HTML and all new lines to make sure the excerpt only contains text.

Building the Site

To build your site, navigate to your website folder (cd websites/my-cool-website), then run jekyll build, wait for it to complete and check out the _site folder. Another big advantage is that you can play around with your site as much as you like, build it locally, test it and if you like it, decide that it's time to upload it.

The most important commands

  • jekyll build
  • jekyll build --drafts — include drafts
  • jekyll build --watch — automatically rebuild if there are changes
  • jekyll build --safe — disable custom plugins
  • jekyll serve — creates a preview of the site at http://localhost:4000/
Screenshot of terminal output after successful serve

Of course you can also combine the flags. jekyll serve --drafts --safe for example previews your site with drafts and without custom plugins. The simplicity of the commands makes it very easy to integrate the build process into your existing workflow – and a lot of people have created excellent tools based on it, for example this very versatile Jekyll Package for Sublime Text that lets you build your site straight out of your editor and adds a bunch of helpful shortcuts.

Uploading the site

The easiest way to upload your site is to simply copy the contents of your _site folder to the right location on your FTP server. I personally like using Glynn which also comes as a gem and lets you store your FTP login details in your _config.yml – so all you have to do is type glynn and it automatically builds your Jekyll site and uploads it to your server.

Plugins

Before going static, I was worried about all the seemingly "dynamic" elements of my blog, like my RSS feed. But of course, that was kinda stupid. Every time my site is updated and rebuilt, it also rebuilds my RSS feed, making sure it stays "dynamic". There are a bunch of additional plugins and third-party services you can use (sparingly) too add custom functionality. Here are some of my favourites:

  • RSS Feed Generator — probably the easiest way to add a simple RSS feed to your site
  • Jekyll Press — automatically minifies HTML, JavaScript and CSS files
  • Disqus – adds a modern comment system to any site (click here for a Jekyll tutorial)

When it comes to plugins, an important question to ask yourself is: Do I actually want this and if so, does it make sense to install a plugin for it? WordPress comes with an enormous range of built-in functionalities and an almost ridiculous amount of random easy to install plugins on top of that, including a lot of useless and badly developed stuff. This has resulted in people running simple blogs with 50+ plugins and completely losing touch with their CMS.

For example, many users don't think twice about using comments because it's built in by default, even though their blog might not actually need it. Similarly, adding simple share buttons to a post should never require a plugin, especially considering how fucking easy it is to add and style them yourself.

Tips & Tricks

Since you can basically build your template and the underlying logic from scratch, you can easily recreate whatever you liked the most about your previous CMS. Here are a few simple tips and tricks.

Pretty URLs for pages and RSS feeds

If you only have very few pages, it might seem annoying to save them as the index.html of their own folder, just so you can use domain.com/about. To automatically rewrite the URLs, add this to your .htaccess file on your server (or create one – go here for more info and tips):

<IfModule mod_rewrite.c>
RewriteRule ^feed$ feed.xml [L]
RewriteRule ^about$ about.html [L]
</IfModule>

Each request to domain.com/about will now automatically show the file about.html. By the way, this is also very similar to how WordPress or your old CMS did it!

Dynamic page titles

Sometimes you want to display a different page title, depending on whether you're on the home page or a blog post or page. Simply add this to your site's <head>, in my skeleton theme, this is in the header.html:

<title>
{% if page.title %}
  {{ page.title }} | {{site.name}}
{% else %}
  {{ site.name }}
{% endif %}
</title>

In human language, here's what it says: If a page.title exists (which must mean that we're one a blog post or page), display it followed by the title of the site. For example: "This is a blog post | My Blog". Otherwise, just displays the site.title. For example: "My Blog".

Style posts differently based on category

To add tags or categories to your posts, simply add a list of them to the Front Matter block at the top of your post:

categories: [ 'web', 'code', 'tutorial' ]

To add classes named category-web, category-code and category-tutorial to the post, you can simply do this within your loop in the index.html:

<article class="{% for category in post.categories %}category-{{category}} {% endfor %}">
<!-- your post stuff goes here -->
</article>

This goes through every category that's assigned to the post, and adds a class named category-name for each one. In your CSS you could then do something like .category-web { background: blue; }.

Separate previewing from publishing

Once you like the look and feel of your site and decide to publish it, you will have to change the site URL from localhost to your website's URL in your config file. But changing the paths back and forth between previewing and publishing can be pretty annoying and lead to errors (like accidentally publishing your site with the wrong links).

A simple trick to solve this problem is to use two different config files. Here's the command I use to preview and test my site:

jekyll serve --config _config_local.yml --drafts --safe

The _config_local.yml file uses the local paths. When I run jekyll build, it uses the default _config.yml with my domain set as the URL. This system also makes it possible to add other settings and variables to your local preview version.

For example, you could add preview: true to your local config file to easily preview and test new parts of your theme locally. If you build the site using the regular config file, those parts won't even be rendered and created. You can do this by using template logic:

{% if site.preview %}
  stuff that's only displayed when the site is previewed locally using _config_local.yml
{% endif %}]

Combine commands and create your own

The more time I spent working with Jekyll, the more I appreciated the simplicity of the command line build process. Using separate programs to compile the code, optimise the images, upload the files and so on suddenly seemed so inefficient compared to typing in a simple command.

The perfect example: Before building and uploading my site, I want to check the images and compress them if possible. This ImageOptim adddon for the command line lets you do that by simply typing something like imageoptim --directory assets/img.

You don't want to end up with 10 different commands you have to remember and type in to publish your blog so we're simply gonna create our own. This, once again, is surprisingly easy. All you have to do is create a text file, name it something like publish-blog (no file extension necessary) and add your list of commands:

#!/bin/bash
imageoptim --directory assets/img
jekyll build

This runs ImageOptim in our image folder and then builds the site. If you're on a Mac, place the file in the /usr/local/bin folder (you can easily get there by clicking "Go to" in the Finder and "Go to folder..."). To turn it into a command, we just need to change the file permissions and make it executable using this command:

chmod +x /usr/local/bin/publish-blog

Now all you have to do is type publish-blog into the command line!

Helpful Links & Resources

  • The official Jekyll Docs for even more details and in-depth explanations
  • My very simple skeleton template for a personal blog to get you started and help create your first custom blog design
  • Jekyll Now helps you set up Jekyll with GitHub Pages in only a few minutes – without touching the command line
  • Jekyll Cheatsheet is a nice and clean list of the most important formatting rules
  • Liquid for Designers explains the templating language Liquid (which is used in Jekyll templates) and all its possibilities in detail
  • StaticGen lists over 100 static website generator and a lot of interesting alternatives

Latest Posts