Verne
May 11, 2026 · View on GitHub
A minimal static site generator written in V, with a Tera-inspired template syntax and a dual V/HTML shortcode system. Verne defines its own small, explicit DSL:
{{ value | filter }}for output,{% statement %}for control flow,- 21 display-only filters (no arithmetic, no set, no macro, no extends),
- a dual shortcode model: register a function in V or drop an
templates/shortcodes/<name>.htmlpartial — V wins when both exist, - all aggregation, sorting, and remote fetching happens at build time in V,
exposed on
site.*andpage.*. Templates stay strictly display-only.
The full documentation is published at
www.verne-ssg.org; the source lives under
docs/ in this repo (build it locally with mise run build-doc).
I've developed it to build my personal technical blog (labs.davlgd.com) with the
terminal-garden theme. It's now available as an independent project.
Why
Verne exists to:
- ship a single self-contained binary under 2 MB (no Go, no node, no asset pipeline),
- make the template language small enough to read in one sitting (www.verne-ssg.org/reference/templates/),
- let other projects embed the engine as a Tera-style template library,
- stay readable in an afternoon.
Install
git clone https://github.com/davlgd/Verne
cd Verne
mise run prod # or: v -prod -o verne src/
./verne version
The resulting verne binary is fully self-contained and weighs under 2 MB.
Runtime dependency: chroma
Code-fence highlighting is delegated to the
chroma command-line binary
(see installation instructions).
verne build (and verne server when it triggers an implicit build) checks at
launch that chroma is runnable; otherwise the command exits with a pointer
to the install docs. Compiling Verne itself does not need it.
By default, Verne looks for chroma on $PATH. The $VERNE_CHROMA_PATH environment
variable overrides that lookup with a directory; use VERNE_CHROMA_PATH=. when
chroma sits next to the verne binary:
VERNE_CHROMA_PATH=/opt/tools/bin verne build
VERNE_CHROMA_PATH=. verne build # chroma alongside ./verne
Usage
From a Verne site root (verne.yaml, content/, themes/, static/):
verne build # render the site to ./public/
verne server # serve ./public/ on http://localhost:1313
verne server -p 8080 # custom port
Template syntax (mini-Tera)
{{ page.title | upper }}
{% if site.posts %}…{% endif %}
{% for p in site.posts %}{{ p.title }}{% endfor %}
{% with shareurl = page.permalink | urlencode %}…{% endwith %}
{% include "partials/head.html" %}
{% shortcode note level="warn" %}body…{% endshortcode %}
Whitespace control: {%- / -%} trims surrounding whitespace.
trim_blocks and lstrip_blocks are on by default — a statement that
sits on its own line leaves the surrounding lines clean.
Full reference: www.verne-ssg.org/reference/templates/.
Shortcodes
Two ways to define a shortcode <name>:
- V — call
engine.register_shortcode("name", fn (ctx, args) !string { … }). - HTML — drop
templates/shortcodes/<name>.html. Inside,argsandbody(alwaysSafeString) are pre-bound.
V wins when both exist. Full reference: www.verne-ssg.org/reference/shortcodes/.
Layout
Verne/
├── src/
│ ├── main.v # CLI entrypoint
│ └── modules/
│ ├── template/ # mini-Tera engine (lex / parse / eval / filters)
│ ├── render/ # asset bundling + page render + sitemap/RSS
│ ├── content/ # YAML frontmatter + Markdown + page graph
│ ├── mdrender/ # CommonMark renderer + chroma highlighter
│ ├── assets/ # SHA-256 fingerprint + SRI integrity bundler
│ └── … # config, frontmatter, remote, yaml, meta
├── docs/ # the www.verne-ssg.org documentation site (Verne content + theme)
└── examples/
├── _shared/ # shared front-end utilities (kbar, theme toggle, scroll-spy)
├── verneblog/ # editorial blog example with the verneblog theme
├── vernebook/ # long-form book example with the vernebook theme
└── vernestart/ # bare-bones starter built around the default theme
Examples
Three full sites live under examples/
and double as theme references:
verneblog/— editorial blog with theverneblogtheme.vernebook/— long-form book with chaptered navigation and thevernebooktheme.vernestart/— minimal starter scaffolded byverne init, on the bundleddefaulttheme.
Build any of them with verne build examples/<name>, or run the four
sites (docs + the three above) on ports 1313–1316 simultaneously with
mise run serve-all.
Tests
mise run test
Each module ships with *_test.v next to it. The template engine has
31 tests covering parsing, evaluation, filters, shortcodes, and whitespace
control.
License
Apache-2.0 — Copyright 2026 David Legrand (davlgd).
Looking for another Jules V. to read? Have a look at Jules Vallès.