A guided introduction to rendering Go templates from JavaScript.
The template module lets JavaScript scripts render Go text/template and html/template documents without leaving the goja runtime. It is useful for prompts, Markdown reports, HTML snippets, generated configuration, and command output.
The important design choice is that template objects are Go-backed. JavaScript drives the workflow with a fluent builder, but the parsed template set, validation state, render result, and metadata remain typed Go objects.
const template = require("template");
const result = template.text()
.Name("prompt")
.Parse("Hello {{ .Name }}")
.Render({ Name: "Ada" });
console.log(result.Text);
Read fields with PascalCase names because they are exported Go fields.
By default, builders include Sprig and Glazed helper functions. That means common string, date, formatting, YAML, and padding helpers are available inside templates.
const result = template.text()
.Parse("Project: {{ .Project | upper }}\n{{ toYaml .Items }}")
.Render({ Project: "goja-text", Items: ["markdown", "sanitize", "template"] });
console.log(result.Text);
Use .Funcs("none") for a minimal template with no helper presets.
Go templates can contain definitions that are executed by name.
const set = template.text().Name("report").Parse(`
{{ define "title" }}Report for {{ .Project }}{{ end }}
{{ define "body" -}}
# {{ template "title" . }}
{{ range .Items }}- {{ . }}
{{ end }}
{{- end }}
`);
const result = set.RenderTemplate("body", {
Project: "goja-text",
Items: ["Markdown", "Sanitize", "Templates"],
});
console.log(result.Text);
console.log(set.Templates().map((t) => t.Name));
HTML mode uses Go's html/template, which performs contextual escaping.
const result = template.html()
.Parse('<p>{{ .Name }}</p><a href="{{ .URL }}">open</a>')
.Render({ Name: '<Ada>', URL: 'javascript:alert(1)' });
console.log(result.Text);
Use html() for HTML, SVG, or any document where escaping matters. Use text() for Markdown, prompts, plain text, and configuration files.
Use JSFunc(name, fn) when a template needs a small script-local helper. The function is called synchronously during template execution, and thrown JavaScript errors become render errors.
const result = template.text()
.JSFunc("surround", (left, value, right) => `${left}${String(value).toUpperCase()}${right}`)
.Parse('{{ surround "[" .Name "]" }}')
.Render({ Name: "ada" });
console.log(result.Text); // [ADA]
HTML mode still escapes ordinary strings returned by JavaScript helpers:
const result = template.html()
.JSFunc("unsafe", () => "<script>alert(1)</script>")
.Parse("<div>{{ unsafe }}</div>")
.Render({});
Do not use JSFunc for asynchronous work. Keep helpers small, deterministic, and side-effect-light.
The default missing-key policy is error. This makes automation fail loudly when a template expects data that the script did not provide.
try {
template.text().Parse("Hello {{ .Name }}").Render({});
} catch (err) {
console.error("template failed:", err.message);
}
If you need standard Go behavior, choose a different policy explicitly:
template.text().MissingKey("default").Parse("Hello {{ .Name }}");
./dist/goja-text run examples/js/template-demo.js
template.text() for plain text and template.html() for contextual HTML escaping.JSFunc supports synchronous JavaScript helpers; asynchronous Promise-returning helpers are not supported.