Building Static Sites with Jekyll

Building Static Sites with Jekyll

Tutorial Details
  • Topic: Jekyll
  • Difficulty: Easy
  • Estimated Completion Time: 30 minutes

A full-blown CMS is rarely necessary. Sometimes, you only need to create a light, static website … but you have just enough pages to make the process of copying template files and making cross-site markup changes a chore. Today, I’ll demonstrate a simple solution—Jekyll—that will make creating small websites a breeze.


Step 0: Meeting Jekyll

Jekyll is a simple, blog aware, static site generator.

Jekyll is a simple, blog aware, static site generator. That’s what the site says. But, what exactly does this mean? A static site generator is a program that takes a set of files and generates your site with them. As you’ll see, we’ll be able to use a set of templates, create the content files separately, and then use Jekyll to generate our site. The “blog aware” part means that we could use this to create a blog, or any website that has a series of post-like entries (such as a portfolio). Let’s give it a try!


Step 1: Installing Jekyll

Refer here for more information on Ruby Gems.

We’ll begin by installing Jekyll; it’s a Ruby gem, so doing so should be pretty straightforward.

gem install jekyll # use `sudo` if your setup requires it

Yep: it’s that easy. There are a few more pieces we could install if we are planning on doing a more complex set-up, however, since we’re not, this will do the trick.


Step 2: Creating our First Template

Every file or folder that does not begin with an underscore will be copied to the generated site.

Next, let’s set up the folders for Jekyll. Create a folder, called example-app for this tutorial; we’ll be creating a little portfolio site for, say, a photographer. This is a great example of where Jekyll shines: it’s a small site that won’t be updated too frequently, but is large enough to the point that you don’t want to open every page when you need to make a markup change.

Inside example-app, create a folder called _layouts. Notice the underscore at the beginning of this folder: any folder or file that begin with an underscore will not be part of the site that Jekyll generates. If they have a name that Jekyll recognizes (such as _config.yml or _layouts), their contents will be used in the generation of the site, but the files themselves won’t show up in the site. Remember this: every file or folder that does not begin with an underscore will be copied to the generated site (which, by the way, defaults to the _site sub-folder).

So, let’s create a layout. We’ll start with a general site layout that includes all the “chrome” for our site. Create a new file, called default.html inside the _layouts folder (the name doesn’t matter), and add the following code to it:

<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8 />
  <title> {% if page.title %} {{ page.title }} | {% endif %} John Doe, Photographer </title>
  <link rel="stylesheet" href="/css/styles.css" />
</head>
<body>

  <div id="main">

    <header>
      <h1> John Doe Photograghy </h1>
    <header>

    <nav role="navigation">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/portfolio/">Portfolio</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>

    {{ content }}

    <footer>
      <p>@copy; John Doe Photography 2011 | All Rights Reserved. </p>
    </footer>

  </div>
</body>
</html>

A couple of things here to keep in mind…

Firstly, Jekyll uses the Liquid template system (by default). This means, anything you can do with Liquid, you can do in a template in Jekyll. For example, in the <title> tag, we’re using both types of Liquid markup: output markup and tag markup. Output markup may output text (if the variable referenced exists), while tag markup doesn’t. Output markup is delimited by double curly-braces, while tag markup is delimited by a the curly brace / percent sign duo.

The next thing to notice above is what is inside the Liquid tags: things like page.title and content. These are variables provided by Jekyll; you can see the list of available template data in the docs. We can also create custom template data, as we’ll review shortly.

Lastly, notice the CSS we’re linking to: create a css folder in the root of your project and throw this bit of styling into a style.css file:

body {
  font: 16px/1.5 verdana, helvetica-neue, helvetica, arial, san-serif;
  background: black;
  color: #ececec;
  padding: 0;
  margin: 0;
}
ul {
  margin: 0;
  padding: 0;
}
a {
  color: #ccc;
  text-decoration: none;
}

a:hover {
  color: #ececec;
  text-decoration: underline;
}

#main {
  width: 960px;
  margin: 0 auto;
  background: rgba(255, 255, 255, 0.4);
}
header {
  padding: 0 10px;
  overflow: hidden;
}
h1 {
  margin: 0;
}

nav ul, ul.entries {
  list-style-type: none;
}
nav li a {
  float: left;
  margin: 5px; 
}
.content {
  padding: 10px;
}

ul.entries li {
  position: relative;
  margin: 20px auto;
  padding: 20px;
  background: #ececec;
  width: 600px;
}

ul.entries img {
  width: 600px;
}

ul.entries li h3 {
  position: absolute;
  bottom: -18px;
  left: 17px;
  font-size: 2em;
}
ul.entries a {
  color: #ececec;
}
ul.entries a:hover {
  color: #fff;
}

footer {
  font-size: 0.65em;
  text-align: center;
}

Also, create an img folder and add an image, named banner.jpg; we’ll be using it shortly. Any image will do; just crop it to 960px by 300px;.

You might be wondering why we’re using the if statement above if the page.title variable just won’t display if it exists? Well, if it does exists, I want to include the vertical bar after it; another way to write that would be like this:

{{ page.title }}{% if page.title %} | {% endif %}

So, how we do use this template? Well, we need to create a page that will use this template. In the root directory of our project, create an index.html file. Here’s the content:

---
layout: default
---
<section role="banner">
  <img src="/img/banner.jpg" />
</section>

<section class="content">
  <p>
  Welcome to John Doe Photography! Please, check out my <a href="/portfolio/">Portfolio</a> to see my work.
  </p> 
</section>

Here’s the content of our index.html file. Notice what’s at the top of the file: Jekyll calls this YAML front matter. Any file (that doesn’t start with an underscore) that has YAML front matter will be generated by Jekyll before being put in the _site folder (if it has no underscore or YFM, then it will just be copied _site). In this case, the YAML front matter just tells Jekyll what template we want it to use.

Now, open a terminal, cd into your project directory, and run jekyll. You should see something like this:

WARNING: Could not read configuration. Using defaults (and options).
	No such file or directory - /Users/andrew/Desktop/example-app/_config.yml
Building site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site
Successfully generated site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site

Ignore the warning; we’ll come to that shortly. For now, you can see that the site has been built in a freshly-created _site directory. If you open the _site/index.html file in your browser of choice, you should see … a failure. The problem is that our paths (urls and stylesheet) begin with a forward slash. This means we can’t just view them as files, we need to view them on a server. Sure, you could go start up W/MAMP, but why take the trouble? Jekyll has a built in server. So, run jekyll --server, and go to localhost:4000 to see something like image below:

Tutorial Image

If the image above isn’t enough, look at the code of _site/index.html. You’ll see that the template we specified was blended with the content we provided and—voila!—we have our page.

I want to remind you that it’s the YAML front matter that makes this magic happen; if a file doesn’t start with three dashes, one or more lines of properties, and another line of three dashes, the file will just be copied to the _site folder, no generation taking place.


Step 3: Creating a Portfolio Template

Now that we’re comfortable with the basics, let’s create a portfolio for our fictional photographer. Remember how I noted that Jekyll is “blog aware”? Well, we’re going to use this blog-awareness feature to our advantage: instead of posts, we’ll have portfolio entries.

Posts belong in a folder, called _posts, so create that now. The file name pattern for posts must be specific as well: year-month-day-title.ext. Posts — well, any file in your Jekyll site, really — can be either Markdown or HTML.

So let’s make a few posts: remember, these will actually be entries in our portfolio:

_posts/2010-03-04-bikes.md

---
layout: portfolio_entry
image: /img/bikes.jpg
title: Bikes, Black and White
---
Bikes are used by almost everyone in downtown Amsterdam. These ones are chained to a bike rack.

_posts/2010-10-01-wing.md

---
layout: portfolio_entry
title: Wing and a Prayer
image: /img/wing.jpg
---
The wing of the AirBus I rode to England.

_posts/2011-06-05-bridge.md

---
layout: portfolio_entry
title: Stone Bridge
image: /img/bridge.jpg
---
An old stone bridge in London.

_posts/2011-07-09-road.md

---
layout: portfolio_entry
title: Road and Curb
image: /img/road.jpg
---
Bike lanes here are terribly thin.

Pretty simple, eh? Notice how we’re creating a custom YAML front matter field: image. This is the URL to the image for that entry. Sure, we could build the whole entry HTML here in this file, but what if we want to change that? We’d have to return and change it in every entry. This way, we can instead use our portfolio_entry template to render them. What’s that template look like? It’s pretty simple too:

_layouts/portfolio_entry.html

---
layout: default
---

<h2 class="content">{{page.title}}</h2>

<img src="{{ page.image }}" />

{{ content }}

If you looked at the template data page, you’ll know that any custom front matter we add will be available under page; so, here, we can access page.image. We’re also using page.title and content (everything after the last three-dash line).

I should mention here that, while the post title is supposed to be available on the post object, I’ve only been able to get it to work on the page object. Whatever works!

Also, notice that we have this template using our default layout. You can nest templates like that, and make your job even easier.

This gives us our entry (post) pages, but what about the main portfolio page? When writing our navigation in our default layout, I noted that we want it as /portfolio/. So, create a folder, called portfolio in the root directory, and open an index.html file within it.

---
layout: default
title: Portfolio
---

<section class="content">
  <h2>Portfolio</h2>

  <p>Check out my images below!</p>
</section>

<ul class="entries">
  {% for post in site.posts %}

  <li>
    <a href="{{ post.url }}">
      <img src="{{ post.image }}" />
      <h3>{{ post.title }}</h3>
    </a>
  </li>

  {% endfor %}
</ul>

This is our most complicated piece yet. Remember, this isn’t a template: it’s a “normal” file, but it can still include Liquid tags. We start by setting layout to default, and title to “Portfolio.”

Notice that, in the HTML, we have a Liquid for-in loop. We retrieve all the posts with sites.posts; then, we loop over those posts with for post in site.posts / endfor. If you’ve worked with WordPress, or any other blogging system, you should be familiar with the concept of a loop. That’s all this is! Inside, as you can see, we can get the standard properties, as well as any front matter we defined (like image).

Now if we run jekyll --server to re-generate the site and start the server, localhost:4000/portfolio/ should show this:

Tutorial Image

And here’s an entry page:

Tutorial Image

Great! You’ve created a portfolio. I’m sure you see, as well, how this works for a blog. Let’s now move on to look at some configuration options for Jekyll.


Step 4: Writing a Config File

There’s a plethora of options for Jekyll. It’s great that all of them have really sensible defaults, but if you want to change them, it’s not hard at all.

There are two ways to set options.

  • First, when you run the program on the command line, you can pass parameters. We’ve already seen the --server parameter, which starts a server after generating the site.
  • A different way, and the way we’ll use here, is in a config file, called _config.yml; this is a YAML file, so each line is a key: value pair, just like in the YAML front matter. Jekyll will look for this file before generating site.

So, make an _config.yml file, and let’s check out some of the most common options.

For a complete list of options, review the configuration documentation.

  • auto: Adding auto: true to your config file will keep Jekyll running, watching your project folder for changes and regenerating the site on the fly.
  • source: If your source files are in a different directory than the one you’re running Jekyll from, you’ll want to set that directory with the source property.
  • destination: By default, the destination for your generated site is ./_site. If you’d like something different, set it here.
  • permalink: The permalink is the path to your posts. By default, that’s /year/month/day/title.html. However, you can customize that if you want. Among others, you can use the variables :year, :month, :day, :title, and :categories. :categories comes from the front matter; all the others come from the post file name. Then, you can set permalink to things like /:year/:month/:title/ or /:categories/:title.html. Bonus tip: if you have a permalink property in the post front matter, it will override the site-wide default.
  • exclude: Like I said above, Jekyll won’t generate files in directories starting with an underscore. But, if you have folders that you want it to ignore, but that don’t start with an underscore, you can do it with exclude in your config file.

Step 5: Deploying the Site

So, let’s say you’ve created the site, and want to set it free, out on the world wide web. How do you do that?

There are several ways to accomplish this. Of course, if it’s a small site that you won’t be updating too often, then simply FTP it up to your server; this might be your only option, if you’re using shared hosting.

If you’ve got a VPS or dedicated hosting setup, you can run more automatically. Check out the deployment documentation for a list of good ideas. If you aren’t sure what to do, try following the directions for using the git post-receive hook; I’ve tried that, and it’s pretty cool.


Step 6: Taking it Further

This is just the tip of Jekyll.


Conclusion

Well, that’s your introduction to Jekyll – the simple, blog aware, static site generator. The next time you’re building a brochure-style, business-card-y, micro-portfolio site, think you’ll give Jekyll a try? Let me know in the comments and thank you so much for reading!

Tags: Ruby
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • vaff

    Thx Andrew, you have been making some really nice articles lately, and this one is great as well. I’ve seen some posts about using Jekyll and Github for blog / site maintenance, but this one is nice and short. And seams like a really good starter.

  • http://www.geektopia.me Shaun Dunne

    Awesome tutorial Andrew, thanks so much for writing this. Could have used it a week ago when I first began looking at Jekyll – there is some great documentation out there, but it was great to have a quick tut to go from beginning to end.

    I will certainly be looking more into Jekyll now for micro-sites.

  • http://ushcompu.com.ar ushcompu

    Niiiice! My site is build with Cyrax (It’s like Jekyll, but It’s a Python program) :D

  • http://ᴁ.com Alex Eckermann

    Nice post.

    I redid my website from Tumblr to Jekyll a few months ago. I wanted to move away from the flaky-ness of Tumblr onto something that was more customisable, professional but being just as easy to publish to. After some searching Jekyll ended up being the solution.

    The real humdinger feature of my new site is that its hosted on Amazon S3 using the static site feature. It works really well, its fast, never has gone down even with AWS outages and costs me absolutely, almost, nothing. My last bill was 1¢ from just going over the request limit of the free tier.

    If you want a blog or site that you can customise but dont want to mess about with a VPS or hosted solution then I highly suggest giving Jekyll a go.

    Yes, it can be a slight struggle having to rethink how you structure the site since you dont have PHP or Ruby to dynamically control things but it eventually presents really nifty solutions that work better in the long run. Javascript can handle most little operations that need doing as I did with my 404 redirector.

    I published a post after the transition including a few code snippets I used in the Jekyll app to create the category pages and how I overcame some hurdles. Heres the post: http://alexeckermann.com/blog/new-site.html

  • http://twitter.com/c_t_montgomery Connor Montgomery

    Andrew, nice post man! I’d like to point out to the readers that make it this far that Octopress (octopress.org) is an “already prepared” fork of Jekyll. It’s super easy to work with (you can theme it, lots of built-in functionality, etc), and I recommend it to anyone who’s looking to go with a page-based site manager, versus a CMS (which more and more people seem to be doing recently).

    Anyways, as always, great post.

    • http://frederic-hemberger.de Frederic

      Well’ Octopress isn’t exactly a fork of Jekyll. It just wraps Jekyll with a mobile-friendly theme, Solarized styles for code syntax highlighting, widgets for Disqus, GitHub, Twitter, Google Analytics, and a built-in Atom feed.

      What I really love is the easy deployment via Rsync or GitHub pages, which makes publishing a snap. And support for Heroku and Amazon S3/CloudFront is already in the making.

  • Prabhjeet

    Ok, this is good, but i never tried to learn ROR. is there anything similar for PHP guys?

  • vaff

    @Prabhjeet … I think you got it backwards, Jekyll is mostly plain HTML and Markdown. You only need Ruby to build the pages. Then you upload it to some kind of server. If you then use GitHub, Amazon S3 or hell even Dropbox, is pretty much up to you.

    • Prabhjeet

      ok @vaff, I will try it, thanks

  • http://www.thoughtresults.com Saeed Neamati

    Honestly, working with WordPress is much more easier than Jekyll. I never even try again to use it.

    However, thanks for introducing Jekyll.

    • http://www.geektopia.me Shaun Dunne

      I think that Jekyll and WordPress are two completely different things and should’t really be compared. WordPress might be ‘Easier’, but it’s usually over-kill for micro / single page sites. Jekyll is perfect for this, even as a micro blogging platform its great, especially – as mentioned – when you can update via rSync.

      • http://www.cloudbacon.com Braden

        Agreed, WordPress and Jekyll are two completely different solutions. One of the most glaring items being the requirement of a database to hold the structure up (WordPress). I definitely fall into the Ruby/Rails camp but, unless you need the horsepower there is no point in wasting a small site on WordPress.

  • http://phrozn.info Mike Wink

    If someone (@ Prabhjeet) is looking for a PHP based solution, there is Phrozn (http://phrozn.info).

    • Prabhjeet

      Thank you @Mike, I will check it.

    • http://www.a1media.ca Douglas Helmer

      I really wanted to try Jekyll, but not being a Ruby user, I’ll definitely be checking out Phrozn. Thanks for the link :)

  • http://www.ekdhou.com edk

    I have been experimenting with Octopress lately which is another blogging platform based on Jekyll. They include some great plugins that will help with sharing code such as code blocks and pull-ins.

    @Andrew, thank you for producing another simple and ‘to the point’ article. Great work!

  • Dimitrios

    I’m working with WordPress for a couple years now , it’s simple to develop websites of any complexity . Nowadays almost all hosts provide Databases and PHP environments for no cost . I had never heard about Jekyll , Textile and Markdown until today . My first impression is that with Jekyll it’s difficult to get started , I’ll give it a second try .
    Thanks for this article and for all comments .

  • http://www.alurida.com Windows and Doors Accessories

    Thanks very much ! i like this article,i made static pages with Dreamweaver,now i want to try Jekyll …

  • Juan

    Great post.
    Would you please give a tutorial on how octopress works or how to customize your octopress blog.. thank you!

  • http://www.gelang-magnet-kesehatan.blogspot.com/ gelang magnet kesehatan

    Nice info

    The real humdinger feature of my new site is that its hosted on Amazon S3 using the static site feature. It works really well, its fast, never has gone down even with AWS outages and costs me absolutely, almost, nothing. My last bill was 1¢ from just going over the request limit of the free tier.

  • http://jekyllbootstrap.com Jade

    I made a quick-start framework for publishing a blog using GitHub Pages called http://jekyllbootstrap.com. This might be easiest for users getting started with Jekyll and ruby. Lots of documentation.

    • Chris

      Thanks dude, this helped out a little. Now I’m just having an ‘Forbidden’ error when running jekyll –server

      • Chris

        Just kidding… I was in the parent folder when making the server command.

        Thanks :D

    • septerr

      Awesome job, very comprehensive. Bookmarked your site for reference. Thanks!

  • http://www.esylhet.com/ sylhet

    Are there any showcase of this CMS ?

  • Tony

    How do you install jekyll on a shared host?

    • Aurinko

      I just simply FTP the whole site and is been working fine.

  • http://pixilateddesign.com/jodycb/ jodycb

    Thanks for this tutorial. It was really helpful for me as a user brand new to Ruby and Jekyll. I have a much better understanding of how the pieces fit together now. I’m sure I’ll have to make a lot of mistakes before I’ve got it down but this explained a lot of stuff I was confused about.

  • http://www.designdiverso.com Tancredi

    Hi all,

    I appreciated the tutorial although now I managed to create my first website using jekyll and I’m stuck with it, as creating the contact form seems to be the most difficult part and is not even mentioned here.

  • http://www.designdiverso.com Tancredi

    also I’ll offer eternal devotion and gratitude to whoever will publish something consistent and well explained about the topic “integrating a contact form into a jekyll website” as painlessly and thoroughly as possible as it’s impossible at this point to find anything like it online. I’ve found few posts but they didn’t really help me sorting this out.

    • http://jure-stern.si Jure Stern

      Why don’t you just use Wufoo forms? I integrated the form in a snap in the jekyll website, although i’m not hosting the website on GitHub.

      • http://www.designdiverso.com Tancredi

        Hi Jure,

        sounds interesting although I’d rather not have my data bypassing through an external server, also due to security reasons.
        However can you provide an example of where you’ve integrated that form?
        My plan is to host it on heroku if I manage to harness the deployment procedure.

  • Aurinko

    been struggling lately with the blog post listing, the issue is that when I publish the posts with my layout using:

    {% for post in site.posts %}

    Jekyll lists all the posts in the same spot of the layout causing a mess, I wan the posts to be separated from each other (inheriting the styling) with date on each post, how can I accomplish it? Anyone as suggestions?
    This is how the layout should look like http://www.tommyblue.it

  • creamidea

    Usefully

  • Christoph Vigano

    Wow, that was some work. Tried to figure out Jekyll on my own but got lost on the road. Thanks for this tutorial!

  • andrewmunsell

    This is a good tutorial, but I’ve also written a pretty in depth tutorial on my website: http://www.andrewmunsell.com/tutorials/jekyll-by-example

  • http://twitter.com/JasonShen Jason Shen

    Very minor point: it should be “{&}copy;” (without the curly brackets) not “@copy;” for making the copyright symbol in the default HTML code. =)

    Otherwise, super helpful tutorial — really made Jekyll super easy.

  • Tom Grim

    If you’re struggling to get the about and contact pages working then set the permalink in the config file to pretty.

  • Johnathan Smith

    Do you know how to use Jekyll for With External Links

  • http://twitter.com/b3h3m0th b3h3m0th

    I use jekyll and I love it; simple and useful.

  • Denar

    wow this was helpful way more than the official documents and to someone who always hated the server site programming and want to get the best of static haven :)