# Renderer Reference

Render artifacts to different output formats

Canonical URL: https://docs.paradoc.dev/sdk/renderers/

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:

```bash
npm install @paradoc/render
```

## Usage

```ts
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:

```ts
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:

```ts
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](/sdk/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 point                 | Description                                                  |
| --------------------------- | ------------------------------------------------------------ |
| [Text](/sdk/renderers/text) | Deterministic text templating for HTML, Markdown, plain text |
| [PDF](/sdk/renderers/pdf)   | Fill PDF forms with AcroForm field support                   |
| [DOCX](/sdk/renderers/docx) | Generate Word documents from templates                       |

`@paradoc/render/pdf` also exports PDF utilities: `inspectPdf`,
`flattenPdf`, `selectPdfPages`, `mergePdfs`, and the placement helpers. See
[PDF utilities](/sdk/renderers/pdf#pdf-utilities).

## createLayerRenderer()

```ts
function createLayerRenderer(options?: CreateLayerRendererOptions): ParadocRenderer<
  RendererLayer,
  string | Uint8Array
>
```

| Name | Type | Description |
| --- | --- | --- |
| `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:

```ts
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

| Name | Type | Description |
| --- | --- | --- |
| `id` | `string` | Unique identifier for this renderer (e.g., "text", "pdf", "docx") |

**Methods**

| Name | Type | Description |
| --- | --- | --- |
| `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:

| `kind`      | `artifact`  | `data`                                                                                                                         |
| ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `form`      | `Form`      | `FormData`: `fields` holds field values only; `parties`, `annexes`, `signers`, `captures` and computed `defs` travel beside it |
| `checklist` | `Checklist` | `ChecklistData`: item statuses keyed by item ID, `null` when unset                                                             |
| `document`  | `Document`  | none: a document has content, not data                                                                                         |

| Name | Type | Description |
| --- | --- | --- |
| `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:

| Name | Type | Description |
| --- | --- | --- |
| `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:

| Name | Type | Description |
| --- | --- | --- |
| `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

```ts
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')
    }
  }
}
```

## Related

* [Formatting](/sdk/formatting) - Value formatting for renderers
* [Form Layers](/sdk/artifacts/form) - Template layer configuration
