software projects by ryan davis


Stupid fast static website generation

Table of Contents:

zenweb 3 is a complete rewrite inspired by Jekyll. Jekyll was designed for github pages and does a great job of it. Unfortunately these design decisions don’t apply to the rest of us. Jekyll is painfully slow because it always builds everything and it only allows liquid for templating which unnecessarily restricts our ability to implement features.

zenweb 3 overcomes these issues. It uses rake to manage dependencies and only builds what is needed. It allows for any markup language to be used at any level so you’re not stuck with liquid or anything else you might not like. Extra markup languages are provided via a simple rubygems plugin system.


Getting Started

Creating a New Site

You can now modify the Rakefile and set HTML_DIR to point to the base directory of your website. If the server is remote, prepend the hostname with a colon:


Once this is set, rake push will use rsync to publish your website quickly and efficiently. See the tasks below for more info of what you can do via rake.

Using zenweb

zenweb is typically driven entirely through rake tasks. Out of the box it provides the following tasks:

Run a webserver and build on the fly.
Publish your website: clean, generate, push
Generate the website.
Push changes to the site.
Remove junk files.
Clean and also remove the generated .site directory.
Debug the generation of a file.


Creating a new page is as simple as creating a new file. Really. That’s all there is to it. You can provide an optional YAML header but the rest of the file is just content. File extensions are used to determine how to render the file and stop rendering when they get to a file extension they don’t know.

For example, say you have the file pages/ and it looks like this:

title: My Pages

This is my website. There are many like it but this one is mine.

{{ sitemap }}

Broken down:

Lines 1-3
The YAML header and can define anything you want. Here we only define the title of the page. Other configuration values like layout can come from inherited _config.yml files.
Line 5
One simple line of markdown content.
Line 7
Ruby code that creates a sitemap for all pages below this one.

When it comes time to generate this file, render_erb will be called first because it is the last extension on the filename. Next, render_md will be called. Since there is no render_html, rendering stops at this level and the resulting filename will be .site/pages/index.html.

When it comes time to generate this file will call render_erb resulting in will call render_md resulting in index.html. There is no render_html, so rendering stops at this level. The resulting filename will be .site/pages/index.html.

When it comes time to generate this file:

input filename renderer output filename
pages/ render_erb pages/
pages/ render_md pages/index.html
pages/index.html N/A .site/pages/index.html

But it doesn’t stop there… If layout is defined, then _layouts/<layout-name>* is looked up. The layout is rendered and the contents from your page is injected into the body of the layout. If a layout defines a layout, then the process repeats. This allows you to organize your site as site-layout(section-layout(page-contents)) or however you please.

File Organization

File organization in zenweb is pretty easy and flexible. There is only 1 file required to get started: the Rakefile. It needs to contain:

Rake.application.rake_require "zenweb/tasks"

and nearly everything else is taken care of.

A simple website might contain the following:

Mentioned above.
All generated files are written to .site.
The top level configuration (yaml) file.
Snippets to be used by include statements are stored here.
Layout files are stored here.
The homepage of your site.
Subdirectories can have their own config files and can override values higher up.
A typical blog directory will contain an index file and a bunch of dated posts. These files all render using ERB and markdown.
Regular web pages and subdirectories. zenweb isn’t biased towards blogs.
Generates an Atom feed file for feed reader applications. Uses ERB to generate.
A sitemap.xml file to help search-engines index your content.
A simple static robots.txt file

Comparing to Jekyll

zenweb is quite a bit lighter than jekyll. This can be seen as both a good thing and a bad thing. In some ways, zenweb does less than jekyll. In some ways this gives zenweb more flexibility and certainly more speed.

To objectify this comparison, we can look at the number of dependencies:

gem deps
Jekyll 27
zenweb 8


Things that zenweb borrowed from jekyll:

Things zenweb doesn’t do out of the box (yet?):

Things zenweb has that jekyll doesn’t:


Build Type Jekyll zenweb
Full Build 146.2s 26.6s
Build 1 page 148.2s 4.9s
Build 0 pages 147.3s 2.3s

Updating a single page on my website (~300 pages) and rsyncing all changes to the server takes me a mere 3.3 seconds with a single rake sync invocation on a semi-slow café network.

Get The Code

If you just want to use zenweb, you can install it via RubyGems:
gem install zenweb
Fork me on GitHub If you want to hack on zenweb, clone it from GitHub:
git clone git://

Latest Activity