github.com/ralsina/nicolino
0.27.0 / published Sep 9, 2026 / repository
A good SSG written in Crystal
Nicolino
A good static site generator.
Features
- Markdown-based content - Write in markdown, get HTML
- Posts and pages - Blog posts with RSS feeds and static pages
- Taxonomies - Tags, categories, and custom classification systems
- Image galleries - Automatic thumbnails with lightbox
- Books - mdbook/gitbook-style documentation with hierarchical chapters, sidebar TOC, and navigation
- Search - Site search functionality
- Sitemap - Automatic XML sitemap generation
- Lua filters - Custom template filters in Lua, no recompiling needed
- Fast builds - Parallel, incremental builds via Croupier task system
Custom Filters in Lua
Themes can ship template filters written in Lua — and so can your site:
put scripts in filters/ at the project root (like shortcodes/) or in
themes/<theme>/filters/*.lua. Site-level scripts win name collisions.
Each script returns a table of functions:
-- themes/mytheme/filters/text.lua
return {
shout = function(text)
return string.upper(text) .. "!"
end,
}
Every function becomes a Jinja-style filter usable in any template:
{{ title | shout }}
The piped value arrives as the first argument, extra filter arguments
follow ({{ page.title | truncate(80) }} calls truncate(title, 80)).
Strings, numbers, booleans, arrays and dictionaries marshal in both
directions; integral numbers come back as integers.
Notes:
- Scripts run with Lua's full standard library, so themes are trusted
code — review
filters/before installing third-party themes. - Filter changes are picked up automatically by
nicolino auto. - Building requires the Lua development headers (
liblua5.4-devor equivalent, same as any other build dependency).
Performance
On the 4000-page build benchmark the release build is about 20% faster than Hugo, and the dev build stays comfortably usable. On a 12-core machine the dev build renders this site from scratch in under a second:
time bin/nicolino build --fast-mode -B -p -v 3
# ≈ 0.9s (cold/full rebuild)
When nothing changed, the incremental build is near-instant:
time bin/nicolino build --fast-mode -p -v 3
# ≈ 0.2s (warm, no-change)
Results are published to each CI benchmark run; see
bench/README.md for methodology and the reproducible
harness.
WARNING
This project is still in development and may change suddenly in places like the configuration file format, but it is ready to start being used.
For more information, visit https://nicolino.ralsina.me
Building from Source
Building requires libvips and Lua development headers to be available on
your system:
- macOS:
brew install vips lua - Debian/Ubuntu:
apt install libvips-dev liblua5.4-dev - Fedora:
dnf install vips-devel lua-devel - Arch:
pacman -S libvips lua
Building for Release (Static Binaries)
The project uses libvips for fast image processing, but libvips cannot be
statically linked. To create static binaries, we use the -Dnovips flag which
falls back to crimage (a pure-Crystal image library).
Important trade-offs:
- Non-release builds with crimage are very slow - image processing will take noticeably longer
- Release builds with crimage are somewhat faster - but still slower than libvips
- Static builds require
-Dnovips- otherwise linking will fail due to missing libvips static libraries
For development use, build without -Dnovips for fast image processing with
libvips. For release/static builds, use -Dnovips and accept the performance
trade-off.
API
- Archive
- Assets
- Base16
- Books
- BuildLock
Exclusive lock guarding commands that write output/ and the task store: two nicolino processes running concurrently (e.g.
- Config
- ContentScanner
- Creatable
- DateUtils
Date parsing utilities
- FeatureTask
A Croupier::Task subclass that automatically tracks timing per feature
- FeatureTiming
- FolderIndexes
- Gallery
Create automatic image galleries
- HTML
Provides HTML escaping and unescaping methods.
- Handler
- Highlight
Server-side syntax highlighting for markdown code blocks.
- HtmlFilters
Functions that take a Lexbor document and return a modified version To create a Lexbor document, use
Lexbor::Parser.new(html) - Image
- Images
- Import
Import module
- Lifecycle
Content lifecycle management for posts (issue #46)
- LinkChecker
Link checker for validating in-site links
- Listings
- Locale
- LuaFilters
User-defined template filters written in Lua.
- Markdown
- Nicolino
- Pages
- Pandoc
- Posts
- RSSFeed
- Render
- Sc
- Search
- Similarity
- Sitemap
- Taxonomies
- TemplatePreprocessor
Preprocesses Crinja templates at load time to cut per-page render cost:
- Templates
- Theme
- ThemeAssets
- Toc
- Utils
- Video
Create output for video files anywhere in content/