statica
Just HTML. A blazingly fast static site generator that builds on just HTML
Full reference: docs/guide.md · Man pages: docs/man/
Install
Rust (crates.io):
JavaScript (npm):
Create a new site directly from npm:
Homebrew:
Prebuilt macOS and Linux binaries from GitHub releases. See homebrew/README.md for tap setup.
# package.json scripts: "build": "statica build ."
From this repo (dev):
Quick start
Or use the installed CLI:
statica new creates a small localized starter with a centered logo, Guide and GitHub links, and English/French/Portuguese catalogs.
&&
CLI
statica [PATH] build + watch + serve (default)
statica build [PATH] one-off build
statica serve [PATH] preview out_dir with 404 fallback
statica watch [PATH] watch + rebuild + serve
statica new <NAME> scaffold
statica -h / --help
statica -v / --version
Project location: PATH (default .) → resolve against process cwd → walk up for statica.toml → site root is that dir, or project / --project under it.
Nested config tables use compact SPECs (CLI wins over the file):
Man pages
Config (statica.toml)
Optional. Missing file → defaults. See docs/guide.md for the full reference.
= "" # relative to this file; empty = here
= ".website"
= "" # needed for sitemap / RSS
[]
= false
= true
= true
= true
[]
= false
= 50000
[[]]
= 10
= "published_at"
= true
= true
[]
= false
= 50
[]
= "auto"
= 0
[]
= "0.0.0.0"
= 4321
[]
= false
= ["en"]
| Asset kind | Tool |
|---|---|
| CSS | lightningcss (nesting, modern syntax → browser-ready; minify with --process or --minify) |
| JS | oxc |
| HTML | minify-html (final pass with --minify) |
| Images | oxipng + image |
| Fonts | copied as-is |
Inline <style> (pages + fragments) is always transformed. Linked .css under asset_dirs is transformed when [process].css is on. Enable [minify] / --minify for a final pass on emitted HTML, CSS, and linked JS; inline scripts are preserved so scoped fragment behavior stays exact.
Set [performance].render_mode to auto, serial, or parallel. serial avoids rayon for page rendering; parallel always uses rayon; auto uses statica's default page-render profile. Use render_threads = 0 for the default worker count, or set --render-threads N to cap parallel page rendering.
Use --report-json [PATH] to write the build report as JSON for benchmarks, CI, and integrations. Omit PATH or pass - to write JSON to stdout; pass a file path to update that file. In watch, the report is written after the initial build and each rebuild.
statica watch performs conservative incremental rebuilds. Direct edits to an existing page index.html re-emit only that page route when global post-processing is off. Changes to shared inputs such as data, fragments, assets, config-driven processing, deleted files, or minified builds fall back to a full rebuild.
Authoring
statica source is valid HTML. It uses normal <template>, <slot>, and <link> elements as build-time authoring primitives, so keep them where HTML allows them.
index.html → .website/index.html
404/index.html → .website/404/index.html
posts/[slug]/index.html → .website/posts/{item.slug}/index.html
blog/[page]/index.html → .website/blog/1/, blog/2/, … ([[pagination]])
If the site does not define 404.html or 404/index.html, statica writes a default .website/404/index.html. Custom 404 pages are normal source pages and always win. statica serve returns the 404 page with HTTP status 404 for missing paths.
@Google is a normal URL alias from [aliases.urls]; the Google Fonts preconnect behavior is handled by a built-in font recipe after alias resolution.
- Scalar page text →
data-t="${item.field}", or literal text withdata-t="Plain text" - Attributes →
${item.slug}/${page.pagination.next_href}/${i18n.locale} - Dynamic data
hrefvalues use the same scoped attribute rules, so locale data uses paths likehref="../content/posts.${i18n.locale}.json"after binding{i18n} - Fragment mount:
<slot id="fragment-id"></slot>passes the current item/context - Fragment loop:
<slot id="fragment-id" data-each="items"></slot>passes each item - Fragment scripts are scoped by default; inside a fragment
<script>,document.querySelector,document.querySelectorAll, anddocument.getElementByIdsearch only that fragment instance - Page
data-binddeclares canonical roots such as{item},{page},{data}, or{i18n}before use - Data link IDs are directly available by
id; they cannot be nameddata,item,page, ori18n - Collection: linked data +
[slug]; current record isitem - Pagination: linked data +
[page]; page chunk ispage.pagination - Fragments never receive canonical page context; pass values through the mount context or link fragment-local data
Keep content funnels build-time. Production output should not fetch site data at runtime unless you are intentionally adding unrelated client behavior.
Crate layout
crates/statica-cli— CLI (cwd/project resolve, config, SPECs, watch/serve, man pages)crates/statica— discover → funnel → bind → scope → emitexamples/blog— dogfood fixturedocs/— guide + man pages
License
MIT
Author
(c) 2026 Simão Nziaka