Jackdaw ⚡️
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
Features
- ⚡️ 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
Performance
| 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 |
Installation
Add this line to your application's Gemfile:
gem 'jackdaw'And then execute:
$ bundle installOr install it yourself as:
$ gem install jackdawQuick Start
Create a new site
$ 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)
Build your site
$ jackdaw build # Build the site
$ jackdaw build --clean # Clean build (remove old files)
$ jackdaw build --verbose # Show detailed outputDevelopment server
$ 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!
Create content
$ 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.mdList available templates
$ jackdaw template listProject Structure
Jackdaw 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
Content files use a naming convention: <name>.<type>.md
Pages
# About Us
Regular pages using the page template.File: about.page.md → Output: about.html
Blog Posts (with date prefixes)
# 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
Metadata Extraction
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
Templates
Layout Template (layout.html.erb)
<!DOCTYPE html>
<html>
<head>
<title><%= title %> - <%= site_name %></title>
</head>
<body>
<%= render 'nav' %>
<%= content %>
</body>
</html>Page Template (page.html.erb)
<main>
<%= content %>
</main>Blog Template (blog.html.erb)
<article>
<h1><%= title %></h1>
<time><%= date.strftime('%B %d, %Y') %></time>
<%= content %>
</article>Partials
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' %>
Template Variables
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 |
Syntax Highlighting
Code blocks are automatically highlighted:
```ruby
def hello
puts "Hello, World!"
end
```Creating Custom Templates
- 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 blogCLI Commands
jackdaw new <name>
Create a new site project with starter templates and example content.
$ jackdaw new my-blog
$ cd my-blog.sitejackdaw build [options]
Build the static site.
Options:
-
--clean, -cClean output directory before building -
--verbose, -vShow detailed output
$ jackdaw build # Incremental build
$ jackdaw build --clean # Full rebuildjackdaw serve [options]
Start 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-refreshjackdaw create <template> <name> [options]
Create 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.mdjackdaw template list
List all available templates.
$ jackdaw template listjackdaw version
Show Jackdaw version.
$ jackdaw versionHow It Works
Build Process
- 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/
Incremental Builds
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.
Live Reload
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
Best Practices
Content Organization
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
Template Naming
- 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)
Dated Content
Use dated types for time-based content:
-
blog- Blog posts -
post- General posts -
article- Articles -
news- News items
Use non-dated types for static content:
-
page- Pages -
project- Projects - Any custom type
Deployment
Jackdaw generates static HTML files in the public/ directory. Deploy to any static host:
Netlify / Vercel
$ jackdaw build
# Deploy public/ directoryGitHub Pages
$ jackdaw build
$ cd public
$ git init
$ git add .
$ git commit -m "Deploy"
$ git push origin gh-pagesNginx / Apache
Copy public/ contents to your web server's document root.
Troubleshooting
Server won't start
Make sure you're in a .site directory:
$ jackdaw serve
✗ No site directory found. Run this command from a .site directory.Template not found
List available templates:
$ jackdaw template listBuild errors
Run with verbose flag to see details:
$ jackdaw build --verboseDevelopment
After 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.
Contributing
Bug reports and pull requests are welcome on GitHub.
License
The gem is available as open source under the terms of the MIT License.
Credits
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.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jackdaw.