JavaScript API
Precompiling
Compile your templates to JavaScript at build time.
Precompiling turns the templates in your codebase into JavaScript modules when you build your site. They render the same HTML, with the same errors, but faster. See the benchmarks.
Kiln never generates code while your site runs, so precompiled templates work everywhere, including Cloudflare Workers.
Precompiling at build time
Call precompile on an environment for each template, and save what it returns as a .js file. Do this in a build script or a bundler plugin.
import { mkdir, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { createKiln } from "@lucidkiln/core";
import { fileLoader } from "@lucidkiln/core/node";
import { options } from "./src/kiln-options.ts";
const loader = fileLoader({ root: "src/templates" });
const kiln = createKiln({ ...options, loader });
for (const name of await loader.list()) {
const file = `build/templates/${name.replace(/\.kiln$/, ".js")}`;
await mkdir(dirname(file), { recursive: true });
await writeFile(file, await kiln.precompile(name));
}
precompile throws a KilnSyntaxError if the template has a problem, so a broken template fails the build instead of a page.
Rendering precompiled templates
Import the modules and give them to memoryLoader in place of the template source. Everything else stays the same.
import { createKiln, memoryLoader } from "@lucidkiln/core";
import layout from "./build/templates/layouts/base.js";
import home from "./build/templates/pages/home.js";
import { options } from "./kiln-options.ts";
const kiln = createKiln({
...options,
loader: memoryLoader({ "layouts/base": layout, "pages/home": home }),
});
const html = await kiln.render("pages/home", { title: "Home" });
With Vite, import.meta.glob can import every module at once:
import type { PrecompiledTemplate } from "@lucidkiln/core";
const modules = import.meta.glob<PrecompiledTemplate>(
"./build/templates/**/*.js",
{ eager: true, import: "default" },
);
const templates = Object.fromEntries(
Object.entries(modules).map(([path, module]) => [
path.slice("./build/templates/".length, -".js".length),
module,
]),
);
const kiln = createKiln({ ...options, loader: memoryLoader(templates) });
Your own loaders can return a precompiled module from load too, in place of the source.
Use the same options
A precompiled template is built for the options it was compiled with. Create the environment that renders it with the same autoescape setting, and the same functions, filters and tests. If something the module depends on is different, such as a built-in filter you replaced with your own, or the generated-code format is incompatible, rendering it fails with a precompiled-mismatch error. Precompile again to fix it.
Build and render with the same exact version of @lucidkiln/core, and regenerate your modules whenever you upgrade it, including patch releases. Generated JavaScript and its runtime interface are build artifacts, not a stable public API. Kiln checks a format version and rejects known mismatches, but that check does not guarantee that modules from different package versions are compatible. Source templates and the documented public API follow semantic versioning.
Only precompile templates you trust
Precompile the templates that live in your codebase, which you review like the rest of your code. Templates written by other people, such as editors in a CMS or AI agents, should load as source. Kiln renders source without generating code, with the same safety rules and limits.
One environment can render both: a loader can return precompiled modules for your own templates and source for everything else.
What a module holds
A precompiled module holds the template's source and the JavaScript Kiln generated from it. Kiln reads the source when it loads the module, so errors point at the right line, and check and analyze work as they do for source.