Engine package
Most projects only need the CLI and the testing helpers. The engine package is for tools that embed template compilation, such as a custom preview in your own admin UI.
@formfeed/engine is the template engine that runs in the Formfeed editor, the CLI and the render servers. It works in browsers and Node 22+, has no Node-only dependencies, and ships ESM and CommonJS builds.
npm install @formfeed/engine
import {
getEngine,
defaultHelpers,
defaultLimits,
assembleDocument,
} from '@formfeed/engine';
const engine = getEngine('liquid');
const compiled = engine.compile('<h1>{{ title | upper }}</h1>', {});
const analysis = engine.analyze('<h1>{{ title | upper }}</h1>');
// analysis.variables → [{ path: ['title'], ... }], analysis.diagnostics → []
const html = await engine.render(
compiled,
{ title: 'Hello' },
{
locale: 'de-DE',
timezone: 'Europe/Berlin',
currency: 'EUR',
partials: () => undefined,
helpers: defaultHelpers(),
limits: defaultLimits,
},
);
const document = assembleDocument({
html,
css: '',
head: '',
settings: { paper: { format: 'A4' } },
kind: 'pdf',
});
What you get
getEngine('jinja2' | 'liquid' | 'handlebars')returns anEnginewith three methods:compile(source, { name })parses a template and throwsEngineSyntaxErrorwithlineandcolumn;analyze(source, { sampleData })returns the variables the template reads and the same diagnostics the editor underlines, and withsampleDataalso warns (unknown-variable) about every variable the data lacks;render(compiled, data, context)produces HTML and throwsRenderErrorwhen the template fails with that data.defaultHelpers()is the helper registry of the helper reference, anddefaultLimitsthe execution limits a render needs.assembleDocument()wraps rendered HTML into the full document Chromium prints, with the print reset and@pagerules from the settings.renderVersion()does all of it for a template version at once: body, header, footer and PDF title.inferSchema(sample)turns sample data into a JSON Schema (draft 2020-12), the way the editor does.renderOffice(file, options)fills a Word or PowerPoint template given as bytes (Uint8Array) withengine,dataand the rendercontext, and returns the filled file with itsformat,warningsanddiagnostics. A document that cannot be filled throwsOfficeTemplateError, whosediagnosticsname the part and paragraph. Pictures and codes need animageshost that loads and rasterises them; without one, only PNG and JPEG data URLs are placed.analyzeOffice(file, { engine, sampleData })lists a Word or PowerPoint template's tags, the variables it reads, its fonts and its findings per part and paragraph, as the app's office template page shows them.starterDocument(engine)andstarterPresentation(engine)return the example Word and PowerPoint files of the app with the sample data that fills them.
The render context
render takes a context with these fields:
| Field | Required | Meaning |
|---|---|---|
locale, timezone, currency | yes | Defaults of the formatting helpers (de-DE, Europe/Berlin, EUR); a template's own settings override them in renderVersion |
partials | yes | Returns the source of an included partial by its name, or undefined when there is none |
helpers | yes | The helper registry, normally defaultHelpers() |
limits | yes | defaultLimits: the time a render may take, the size of its output and the depth of includes |
i18n | no | Translations for the t helper: locale → key → text |
assetBaseUrl | no | Where asset() points; relative references stay relative without it |
brand | no | The brand kit templates see as brand |
assembleDocument({ html, css, head, settings, kind }) takes the rendered body, the template's CSS and <head> content, its settings and its kind (pdf or image); assetBaseUrl adds a <base href>, title a <title>.
The engine produces HTML, or a filled Word or PowerPoint file. Turning it into a PDF happens on the Formfeed servers: HTML in Chromium, so send the document to the API as html, or use formfeed render, to get the exact output; a filled Word or PowerPoint file through POST /pdf/convert.