OpenForm

Renderers

Last updated on

Render artifacts to different output formats

Renderers transform OpenForm artifacts into output formats like text, PDF, and DOCX. Each renderer implements the OpenFormRenderer interface, making it easy to build custom renderers for your specific needs.

Installation

Install all renderers via the umbrella package:

npm install @open-form/renderers

Or install individual renderer packages:

npm install @open-form/renderer-text
npm install @open-form/renderer-pdf
npm install @open-form/renderer-docx

Usage

import { textRenderer } from '@open-form/renderers'
// Or from individual package:
// import { textRenderer } from '@open-form/renderer-text'

import { open } from '@open-form/core'

// Create a form with a template layer
const form = open.form({
  name: 'invoice',
  fields: {
    customer: { type: 'text', label: 'Customer' },
    total: { type: 'money', label: 'Total' }
  },
  layers: {
    text: {
      kind: 'inline',
      mimeType: 'text/plain',
      text: 'Invoice for {{customer}}\nTotal: {{total}}'
    }
  },
  defaultLayer: 'text'
})

// Fill the form with data
const filled = form.fill({
  fields: {
    customer: 'Acme Corp',
    total: { amount: 1500, currency: 'USD' }
  }
})

// Render the filled form
const output = await filled.render({
  renderer: textRenderer()
})
// output: "Invoice for Acme Corp\nTotal: $1,500.00"

Renderer Packages

PackageDescription
TextHandlebars-based text templating for HTML, Markdown, plain text
PDFFill PDF forms with AcroForm field support
DOCXGenerate Word documents from templates

Renderer Interface

All renderers implement the OpenFormRenderer interface. Use this to build custom renderers:

import type { OpenFormRenderer, RenderRequest, RendererLayer } from '@open-form/types'

const myRenderer: OpenFormRenderer<RendererLayer, string> = {
  id: 'my-renderer',
  render(request: RenderRequest): string {
    const { template, form, data, bindings, ctx } = request
    // Your rendering logic here
    return processTemplate(template.content, data)
  }
}

OpenFormRenderer

id: string
Unique identifier for this renderer (e.g., "text", "pdf", "docx")
Methods
render: (request: RenderRequest) => Output | Promise<Output>
Execute the rendering operation

RenderRequest

The request object passed to the render method:

template: RendererLayer
The resolved template layer to render
form: Form
The form schema (used for field type detection)
data: FormData
The data to populate the template with
bindings?: Bindings
Optional mapping from template placeholders to field names
ctx?: OpenFormRendererContext
Optional context with locale, logger, serializers

RendererLayer

The resolved template content:

type: "text" | "docx" | "pdf" | string
Logical template type
content: string | Uint8Array
Template payload (text or binary)
mimeType?: string
Media type (e.g., "text/html", "application/pdf")
bindings?: Bindings
Template-specific field mappings

OpenFormRendererContext

Optional context for customizing renderer behavior:

locale?: string
Locale for formatting (e.g., "en-US")
logger?: { debug?, info?, warn?, error? }
Logger instance for debugging
serializers?: SerializerRegistry
Custom formatters for field values

Building Custom Renderers

To create a custom renderer:

  1. Implement the OpenFormRenderer interface
  2. Handle the template type(s) you support
  3. Use serializers for consistent value formatting
import type { OpenFormRenderer, RenderRequest, RendererLayer } from '@open-form/types'

// Custom CSV renderer example
export function csvRenderer(): OpenFormRenderer<
  RendererLayer & { type: 'text'; content: string },
  string
> {
  return {
    id: 'csv',
    render(request: RenderRequest) {
      const { form, data } = request

      // Generate CSV from form fields and data
      const headers = Object.keys(form.fields || {})
      const values = headers.map(key => {
        const value = data.fields?.[key]
        return value != null ? String(value) : ''
      })

      return [headers.join(','), values.join(',')].join('\n')
    }
  }
}

Related

On this page