Skip to main content

Engine package

Advanced

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 an Engine with three methods: compile(source, { name }) parses a template and throws EngineSyntaxError with line and column; analyze(source, { sampleData }) returns the variables the template reads and the same diagnostics the editor underlines, and with sampleData also warns (unknown-variable) about every variable the data lacks; render(compiled, data, context) produces HTML and throws RenderError when the template fails with that data.
  • defaultHelpers() is the helper registry of the helper reference, and defaultLimits the execution limits a render needs.
  • assembleDocument() wraps rendered HTML into the full document Chromium prints, with the print reset and @page rules 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) with engine, data and the render context, and returns the filled file with its format, warnings and diagnostics. A document that cannot be filled throws OfficeTemplateError, whose diagnostics name the part and paragraph. Pictures and codes need an images host 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) and starterPresentation(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:

FieldRequiredMeaning
locale, timezone, currencyyesDefaults of the formatting helpers (de-DE, Europe/Berlin, EUR); a template's own settings override them in renderVersion
partialsyesReturns the source of an included partial by its name, or undefined when there is none
helpersyesThe helper registry, normally defaultHelpers()
limitsyesdefaultLimits: the time a render may take, the size of its output and the depth of includes
i18nnoTranslations for the t helper: locale → key → text
assetBaseUrlnoWhere asset() points; relative references stay relative without it
brandnoThe 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.