Lightning-fast Ruby static site generator with convention over configuration
Jackdaw is a minimal, fast static site generator that emphasizes:
- Speed: Parallel processing and incremental builds
- Simplicity: Convention over configuration - zero config files needed
- Developer Experience: Live reload, beautiful CLI output, intuitive structure
- ⚡️ Blazing Fast: Build 600 files in under 1 second
- 🔄 Incremental Builds: Only rebuilds changed files (6-18x faster)
- 🎨 Beautiful CLI: Colorful, informative command output
- 🔥 Live Reload: Auto-refresh browser on file changes
- 📝 Markdown + ERB: GitHub Flavored Markdown with ERB templates
- 🎯 Convention-Based: No configuration files required
- 🌈 Syntax Highlighting: Built-in code highlighting with Rouge
- 🚀 Development Server: Built-in server with live reload
| Site Size | Files | Clean Build | Incremental | Files/sec |
|---|---|---|---|---|
| Small | 30 | 0.18s | 0.005s | 5,821 |
| Medium | 150 | 0.35s | 0.012s | 12,343 |
| Large | 600 | 0.87s | 0.037s | 16,280 |
Add this line to your application's Gemfile:
gem 'jackdaw'And then execute:
$ bundle installOr install it yourself as:
$ gem install jackdaw$ jackdaw new my-blog
$ cd my-blog.siteThis creates a project structure:
my-blog.site/
├── site/
│ ├── src/ # Your content (Markdown files)
│ ├── templates/ # ERB templates
│ └── assets/ # Static assets (CSS, images, etc.)
└── public/ # Generated output (git-ignored)
$ jackdaw build # Build the site
$ jackdaw build --clean # Clean build (remove old files)
$ jackdaw build --verbose # Show detailed output$ jackdaw serve # Start server with live reload on port 4000
$ jackdaw serve --port 3000 # Use custom port
$ jackdaw serve --no-livereload # Disable live reloadVisit http://localhost:4000 to see your site with auto-reload enabled!
$ jackdaw create blog "My First Post" # Creates: 2026-01-06-my-first-post.blog.md
$ jackdaw create page "About" # Creates: about.page.md
$ jackdaw create page "company/history" # Creates: company/history.page.md$ jackdaw template listJackdaw follows a simple, convention-based structure:
my-site.site/
├── site/
│ ├── src/ # Content directory
│ │ ├── index.page.md # Homepage
│ │ ├── about.page.md # About page
│ │ └── blog/ # Blog posts
│ │ └── 2026-01-06-hello.blog.md
│ │
│ ├── templates/ # ERB templates
│ │ ├── layout.html.erb # Main layout
│ │ ├── page.html.erb # Page template
│ │ ├── blog.html.erb # Blog post template
│ │ └── _nav.html.erb # Partial (starts with _)
│ │
│ └── assets/ # Static files
│ ├── styles.css
│ └── images/
│
└── public/ # Generated output
├── index.html
├── about.html
├── blog/
│ └── 2026-01-06-hello.html
└── assets/
Content files use a naming convention: <name>.<type>.md
# About Us
Regular pages using the page template.File: about.page.md → Output: about.html
# My First Post
Blog posts automatically get date metadata from filename.File: 2026-01-06-first-post.blog.md → Output: blog/2026-01-06-first-post.html
Jackdaw automatically extracts metadata:
- Title: From first H1 heading (
# Title) - Date: From filename (
YYYY-MM-DD-) or file modification time - Excerpt: First 150 words
- Reading Time: Calculated from word count
<!DOCTYPE html>
<html>
<head>
<title><%= title %> - <%= site_name %></title>
</head>
<body>
<%= render 'nav' %>
<%= content %>
</body>
</html><main>
<%= content %>
</main><article>
<h1><%= title %></h1>
<time><%= date.strftime('%B %d, %Y') %></time>
<%= content %>
</article>Partials start with _ and can be included with <%= render 'name' %>
File: _nav.html.erb
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
</nav>Usage: <%= render 'nav' %>
Available in all templates:
| Variable | Description |
|---|---|
<%= content %> |
Rendered markdown content |
<%= title %> |
Page title (from H1) |
<%= date %> |
Post date (Date object) |
<%= excerpt %> |
First 150 words |
<%= reading_time %> |
Estimated minutes to read |
<%= site_name %> |
Site name (from directory) |
<%= all_posts %> |
Array of all blog posts |
<%= all_pages %> |
Array of all pages |
Code blocks are automatically highlighted:
```ruby
def hello
puts "Hello, World!"
end
```- Create a new template file:
templates/project.html.erb - Add your template code
- Create content with:
jackdaw create project "My Project"
Dated templates (auto-prefix with date):
blog,post,article,news
Override with flags:
$ jackdaw create page "Timeline" --dated # Add date to page
$ jackdaw create blog "Timeless" --no-date # Remove date from blogCreate a new site project with starter templates and example content.
$ jackdaw new my-blog
$ cd my-blog.siteBuild the static site.
Options:
--clean, -cClean output directory before building--verbose, -vShow detailed output
$ jackdaw build # Incremental build
$ jackdaw build --clean # Full rebuildStart development server with live reload.
Options:
--port, -p PORTServer port (default: 4000)--host, -h HOSTServer host (default: localhost)--no-livereloadDisable live reload
$ jackdaw serve # Start on port 4000
$ jackdaw serve --port 3000 # Use port 3000
$ jackdaw serve --no-livereload # No auto-refreshCreate a new content file from template.
Options:
--datedAdd date prefix to filename--no-dateSkip date prefix
$ jackdaw create blog "First Post" # → 2026-01-06-first-post.blog.md
$ jackdaw create page "About" # → about.page.md
$ jackdaw create page "company/team" # → company/team.page.md
$ jackdaw create page "Timeline" --dated # → 2026-01-06-timeline.page.mdList all available templates.
$ jackdaw template listShow Jackdaw version.
$ jackdaw version- Scan: Discover all content, template, and asset files
- Check: Determine which files need rebuilding (mtime-based)
- Process: Render content in parallel using all CPU cores
- Write: Output HTML files to public/ directory
- Copy: Copy assets to public/assets/
Jackdaw uses modification time (mtime) checking to only rebuild files when:
- Content file changed
- Template file changed
- Layout file changed
This makes rebuilds 6-18x faster than clean builds.
The development server watches for file changes and:
- Detects changes using the Listen gem
- Triggers incremental rebuild
- Injects JavaScript that polls for changes
- Refreshes browser automatically
src/
├── index.page.md # Homepage
├── about.page.md # Top-level pages
├── blog/ # Blog posts by category
│ └── 2026-01-06-post.blog.md
└── projects/ # Nested content
└── project-name.page.md
- Main layout:
layout.html.erb(required) - Content templates:
<type>.html.erb(e.g.,blog.html.erb) - Partials:
_<name>.html.erb(e.g.,_header.html.erb)
Use dated types for time-based content:
blog- Blog postspost- General postsarticle- Articlesnews- News items
Use non-dated types for static content:
page- Pagesproject- Projects- Any custom type
Jackdaw generates static HTML files in the public/ directory. Deploy to any static host:
$ jackdaw build
# Deploy public/ directory$ jackdaw build
$ cd public
$ git init
$ git add .
$ git commit -m "Deploy"
$ git push origin gh-pagesCopy public/ contents to your web server's document root.
Make sure you're in a .site directory:
$ jackdaw serve
✗ No site directory found. Run this command from a .site directory.List available templates:
$ jackdaw template listRun with verbose flag to see details:
$ jackdaw build --verboseAfter checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
Bug reports and pull requests are welcome on GitHub.
The gem is available as open source under the terms of the MIT License.
Built with:
- Thor - CLI framework
- Kramdown - Markdown parser
- Rouge - Syntax highlighting
- Parallel - Multi-threading
- Listen - File watching
- Puma - Web server
- Rack - Web server interface
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jackdaw.