SDK

Renderer Reference

Last updated on

Render artifacts to different output formats

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

Installation

@paradoc/core uses the unified renderer automatically. Install it directly when building custom integrations or using format-specific functions:

npm install @paradoc/render

Usage

import { p } from '@paradoc/core'

// Create a form with a template layer
const form = p.form({
  name: 'invoice',
  fields: {
    customer: { type: 'text', label: 'Customer' },
    total: { type: 'money', label: 'Total' }
  },
  layers: {
    text: {
      kind: 'inline',
      mimeType: 'text/plain',
      text: 'Invoice for {{fields.customer}}\nTotal: {{fields.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()
// output: "Invoice for Acme Corp\nTotal: $1,500.00"

Use the optional renderer property to override the default for one call:

const output = await filled.render({ renderer: myRenderer })

The same optional override is available to .seal({ renderer: myRenderer }). Paradoc still applies its built-in PDF flattening and canonical hashing after the custom renderer or conversion adapter returns.

Renderer registry

render(), seal(), assemble(), and sealBundle() also take renderers, a RendererRegistry keyed by layer MIME type. Core consults it when no renderer override is given and before its built-in engines. This is how a format core does not ship reaches the render call.

React layers (text/tsx and text/jsx) render this way. The renderer lives in @paradoc/react-pdf, and reactLayerRenderers builds the entries for both MIME types:

import { reactLayerRenderers } from '@paradoc/react-pdf'

const pdf = await filled.render({
  layer: 'composition',
  renderers: reactLayerRenderers({ components: { 'purchase-order.tsx': PurchaseOrder } }),
})

The React renderer writes the PDF itself, so sealing a React layer needs no seal adapter. See @paradoc/react for the component side.

Format Entry Points

createLayerRenderer() is the default application entry point. Import a format subpath when you need its low-level functions or format-specific configuration.

Entry pointDescription
TextDeterministic text templating for HTML, Markdown, plain text
PDFFill PDF forms with AcroForm field support
DOCXGenerate Word documents from templates

@paradoc/render/pdf also exports PDF utilities: inspectPdf, flattenPdf, selectPdfPages, mergePdfs, and the placement helpers. See PDF utilities.

createLayerRenderer()

function createLayerRenderer(options?: CreateLayerRendererOptions): ParadocRenderer<
  RendererLayer,
  string | Uint8Array
>
formatter?: Formatter
Presentation formatter shared by every supported format.
progressive?: FormatterProgressivePolicy
Text for missing and incomplete values in a preview.
textSignatureOptions?: TextSignatureOptions
Signature rendering options for text layers.
pdfFont?: PdfFont
Font the PDF engine tries before a font declared by the layer.
docxSignatureOptions?: DocxSignatureOptions
Signature rendering options for DOCX layers.
expressions?: { functions?, signatures? }
Host functions available to text and DOCX templates, with their signatures.

Renderer Interface

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

import type { ParadocRenderer, RenderRequest, RendererLayer } from '@paradoc/types'

const myRenderer: ParadocRenderer<RendererLayer, string> = {
  id: 'my-renderer',
  render(request: RenderRequest): string {
    // Narrow on `kind` to read the artifact with its payload.
    if (request.kind !== 'form') return String(request.template.content ?? '')
    const { template, artifact, data } = request
    // Your rendering logic here
    return processTemplate(template.content, artifact, data)
  }
}

ParadocRenderer

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. It names the artifact that declares the layer and carries the payload of that artifact's kind. Narrow on kind to read them:

kindartifactdata
formFormFormData: fields holds field values only; parties, annexes, signers, captures and computed defs travel beside it
checklistChecklistChecklistData: item statuses keyed by item ID, null when unset
documentDocumentnone: a document has content, not data
kind: "form" | "checklist" | "document"
The kind of artifact the layer belongs to
template: RendererLayer
The resolved template layer to render
artifact: Form | Checklist | Document
The artifact definition, matching `kind`
data?: FormData | ChecklistData
The payload of the artifact kind. A document request has none
ctx?: ParadocRendererContext
Optional context with formatter, progressive policy, expressions, and signing markers

RendererLayer

The resolved template content:

type: "text" | "docx" | "pdf" | "react"
Logical template type
content?: string | Uint8Array
Template payload (text or binary). Absent for a layer that only names its content, such as a React composition
mimeType?: string
Media type (e.g., "text/html", "application/pdf")
key?: string
The layer key in the artifact
path?: string
The path a file layer declares
bindings?: Bindings
PDF layers only: AcroForm field name to Paradoc path. Other layers carry none
font?: RendererLayerFont
Resolved font declared by a PDF layer
format?: LayerFormat
Presentation overrides declared by a PDF layer

ParadocRendererContext

Optional context for customizing renderer behavior. A key it does not declare is a type error:

formatter?: Formatter
Formatter selected for the entire artifact render
progressive?: FormatterProgressivePolicy
Text for missing and incomplete values
signing?: SigningMarkerRequest
Set only on the seal pass that must carry flow signature markers
expressions?: RendererExpressions
The artifact's expression context that template expressions read, supplied by `@paradoc/core`

Building Custom Renderers

To create a custom renderer:

  1. Implement the ParadocRenderer interface
  2. Handle the template type(s) you support
  3. Use the request formatter for consistent value presentation
import type { ParadocRenderer, RenderRequest, RendererLayer } from '@paradoc/types'

// Custom CSV renderer example
export function csvRenderer(): ParadocRenderer<
  RendererLayer & { type: 'text'; content: string },
  string
> {
  return {
    id: 'csv',
    render(request: RenderRequest) {
      if (request.kind !== 'form') throw new Error('The CSV renderer renders forms only')
      const { artifact, data } = request

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

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

On this page