Tip
You can now install the Visual Studio Code extension for a more pleasant experience! See other supported IDEs in the integrations section!
The only sane static site generator in existence. Refer to the examples directory for a quickstart.
Here's what it does for you:
- Process SCSS to CSS using grass.
- Render Jinja2 templates with minijinja and optionally poison them.
- Run Lua scripts with mlua, using LuaJIT for the backend. Useful for rendering a template with different sets of inputs.
- Minify HTML/JS/CSS resulting in the build process.
- Leave other files alone and copy them as-is.
Directories are walked recursively depth-first, with files processed and directories read in an alphanumeric order.
Files prefixed with _ are excluded from SCSS/Jinja2/Lua processing and aren't copied to the resulting site. This is useful for:
- Base templates that are meant to be inherited rather than rendered on their own.
- Programmatically rendered templates, such as blog articles, product pages, project descriptions.
- Reused template partials in their own files.
- SCSS
@usemodules. - Lua
require()imports.
Here are some of the sites powered by sanity:
- nonk.dev (repo)
- schwung.us (repo)
- cantsleep.cc (repo)
You can use sanity without ever touching the command-line by installing one of our IDE extensions:
Note
Make sure to add the dist folder to your .gitignore. It doesn't (usually) make sense to version auto-generated files.
Place your Jinja2 templates, SCSS sheets, and Lua scripts inside the www folder. Run sanity from the command line or through one of the integration packages. You should get a fully processed site inside the dist folder right next to www.
You can either upload the contents of dist to a free website-hosting such as Neocities or GitHub Pages, or you can serve them locally using the built-in sanity live-server before pushing the site to production. The details of the latter scenario depend on the integration you're using. If you're unsure, just use VSCode and our integration: this combo runs the live-server automatically once you open your project folder.
Download a binary from available releases. Run without arguments for a one-off build. Run with server to serve your site using the built-in development server; it rebuilds the site whenever the contents of www change. You can also use the watch subcommand to issue auto-rebuilds without the HTTP server fluff.
Discover more options by running sanity with --help.
There isn't much to scripting sanity besides the custom render function. Take a look at this static blog example:
local blog = {
["nice-day"] = {
date = "today",
contents = "I had a nice day today."
}
};
for id, post in pairs(blog) do
render("_article.html", "blog/" .. id .. ".html", {
id = id,
date = post.date,
contents = post.contents,
});
endrender accepts:
- A template name (path relative to
www, without the.j2extension) to add to the render queue. - Output file path relative to
dist. - A dictionary of values to supply to the renderer.
Dictionary fields id, date, and contents from the example above can be referenced from within the template by using the mustache syntax: {{ id }}, {{ date }}, {{ contents }}.
Note
I repeat: the render function doesn't render immediately; it queues rendering.
You can read JSON files inside www by using the json function:
local blog = json("blog/db.json");
-- the rest is the same as the example above...read can be used to store a text file's contents in a string:
local id = "nice-day";
local contents = read("blog/" .. id .. ".md");
-- simileinject can be used to add/modify variables shared across all templates:
inject("last_updated", os.date("%Y-%m-%d"));Let's say you're loading a list of blog articles to render from a really long JSON file, and you want all articles to have a short description field. To ensure each article has such a description field by spitting out an error otherwise, you can use the required filter in your templates:
{% for article in articles %}
<p>{{ article.description | required("All articles need a description") }}</p>
{% endfor }This won't help with figuring out which article is missing a description, but at least you'll be sure all of them have it once you find the culprit.
You can check for the __prod boolean in your templates to exclude analytics & trackers from dev builds:
{% if __prod %}
<script src="https://example.org/tracker.js"></script>
{% endif }Use the lua-lib subcommand to add a LuaLS definitions file to your project folder. This should hide the 999 warnings about undefined functions you've been getting. Make sure to point your IDE to this file, e.g. in VSCode settings.json:
{
"Lua.workspace.library": ["_sanity.lua"]
}Warning
It's a heavily experimental and possibly deprecated feature I pulled out of my ass one night. Don't actually use it in production.
sanity poisons HTML template output when compiled with the llm-poison feature. It is disabled by default. You can suppress the poisoning using the --antidote flag.