NAME
figh — figh interpolates guarded HTML
The name is a recursive acronym. It is the only recursion in the language. See figh(5) for the complete manual.
WHAT IT IS
figh is a template language for templates you didn't write: a customer uploads a theme, and it renders on your infrastructure, next to everybody else's data. It looks like the templating you already know —
<h1>{{ event.name }}</h1>
<figh-if test="tickets">
<ul>
<figh-for each="t in tickets">
<li>{{ t.holder | default: 'unclaimed' }}
— {{ t.price | money: 'eur' }}</li>
</figh-for>
</ul>
<figh-else/>
<p>{{ 'public.event.sold_out' | t }}</p>
</figh-if>
figh's own surface is lit; the host HTML is dim. That is
the whole parser story: figh scans for {{ }} and
<figh-…> and copies everything else through untouched. It
never parses your markup.
WHY YOU'D USE IT
Because the author of the template is not the operator of the server. Every design decision follows from that one assumption: figh is defined by what it refuses to do:
- Emit raw HTML by accident
{{ }}always escapes, with no off switch. The only unescaped output is one greppable element that cannot hide inside an attribute. - Run forever no
while, no user functions, no recursion; the include graph is static and cycles are a compile error. Termination is grammar, not a timeout. - Reach anything no filesystem, no network, no clock, no randomness, no host objects. A template is a pure function of the data you hand it.
- Disagree with itself implemented twice, in Go and JavaScript, with a golden corpus asserting byte-identical output, error columns included. The browser preview cannot drift from production.
HOW TO USE IT
Bind plain data, hand figh a loader, render inside the rails. The same template, the same data and the same output on both sides:
data, err := figh.BindMap(map[string]any{
"event": map[string]any{
"name": "Vector Fest",
},
"tickets": []map[string]any{
{
"holder": "Ada",
"price": int64(24900),
},
},
})
env := &figh.Env{
Data: data, Locale: "en",
}
out, err := figh.RenderGuarded(
"event.figh", loader, env,
figh.Limits{})
const env = {
data: {
event: { name: "Vector Fest" },
tickets: [
{ holder: "Ada", price: 24900 },
],
},
locale: "en",
};
const res = figh.renderToResult(
"event.figh", loader, env);
if (res.ok) preview.srcdoc = res.output;
Everything else (all eight directives, the expression grammar, all fifteen filters, the value model, errors, limits, conformance) is in the manual:
SEE ALSO
figh(5) — the complete language reference.
fighdev/figh — source and
extraction notes. figh runs in production at
Tito; extraction into a standalone module is
in progress, so treat the repo as where the work happens, not yet a
dependency you can add.
🤖 Written by an LLM (Claude), on the ideas, instruction, and editing of humans; see factor X.