docxRendererLast updated on
Last updated on
Render Paradoc artifacts to Word documents
Render forms to DOCX (Word) documents with Paradoc's dependency-light Open XML implementation. It supports placeholders, conditionals, loops, custom delimiters, split Word runs, and automatic field formatting. Every condition, loop source, and placeholder is an artifact expression, so a condition means the same thing in a Word layer as in a field rule.
Example
import { p } from '@paradoc/core'
import { docxRenderer } from '@paradoc/render/docx'
import { createFsResolver } from '@paradoc/resolvers/fs'
import { writeFileSync } from 'node:fs'
const resolver = createFsResolver({ root: process.cwd() })
const form = p.form({
name: 'contract',
fields: {
clientName: { type: 'text', label: 'Client Name' },
contractValue: { type: 'money', label: 'Contract Value' },
startDate: { type: 'date', label: 'Start Date' }
},
layers: {
docx: {
kind: 'file',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
path: '/templates/contract-template.docx'
}
},
defaultLayer: 'docx'
}, { resolver })
// Fill the form with data
const filled = form.fill({
fields: {
clientName: 'Acme Corp',
contractValue: { amount: 50000, currency: 'USD' },
startDate: '2024-04-01'
}
})
// The resolver was bound when the form was constructed, so every instance
// derived from it — filled included — carries it. render() doesn't take one.
const output = await filled.render({
renderer: docxRenderer()
})
// output is Uint8Array - write to file
writeFileSync('contract-filled.docx', output)Import
From the unified package's DOCX entry point:
import { docxRenderer } from '@paradoc/render/docx'API
docxRenderer()
Factory function that creates a configured DOCX renderer instance.
function docxRenderer(options?: DocxRendererOptions): ParadocRenderer<
RendererLayer & { type: 'docx'; content: Uint8Array },
Uint8Array
>Parameters
DocxRendererOptions
DocxSignatureOptions
Returns
Returns an ParadocRenderer object with:
renderDocx()
Low-level function for direct DOCX rendering without the full renderer interface.
async function renderDocx(options: RenderDocxOptions): Promise<Uint8Array>RenderDocxOptions
DocxRenderOptions
Returns
Returns a Promise<Uint8Array> containing the rendered DOCX document.
Template Syntax
DOCX templates use control paragraphs for blocks and {{ }} placeholders inside text. Every expression is an artifact expression:
Contract for {{fields.clientName}}
Total Value: {{fields.contractValue}}
Start Date: {{fields.startDate}}
{{IF fields.discountPercent > 0}}
Discount Applied: {{fields.discountPercent}}
{{ELSE}}
No discount.
{{END-IF}}
{{FOR line IN fields.lines}}
{{index(line) + 1}}. {{line.description}}: {{line.amount}}
{{END-FOR line}}A FOR loop names its row (line above), and index(line), first(line), and last(line) give its position. A FOR paragraph inside a table row repeats the row. Text paragraphs can also use the text-template markers ({{#if}}, {{#each}}), where the row is item.
Supported Features
| Syntax | Description |
|---|---|
{{fields.fieldName}} | Insert a value, formatted for its type |
{{fields.qty * fields.price}} | Insert any expression |
{{IF condition}}...{{ELSE}}...{{END-IF}} | Conditional paragraphs; the condition must be boolean |
{{FOR row IN list}}...{{END-FOR row}} | Repeat paragraphs or table rows |
{{signature(parties.client, "client-sign")}} | Place a signing mark; initials, signatureDate, capacity, and printedName work the same way |
The signing directives print as in text templates, with underlines in place of the bracketed placeholders:
| Directive | Before capture | After capture |
|---|---|---|
signature | _____________________________ | [Signed] |
initials | ______ | [Initialed] |
signatureDate | __________ | the capture's YYYY-MM-DD date |
capacity | the signatory's capacity, else ________________ | the captured text, else the signatory's capacity, else underscores |
printedName | the signer's person.name, else _______________________ | the captured text, else the signer's person.name, else underscores |
signatureOptions.placeholder and signatureOptions.captured replace this text, as they do for text layers. For capacity and printedName, a known capacity or name wins over placeholder.
The old comparison syntax (===, !==, and $row loop names) is no longer accepted: write == and !=, and name the row directly ({{line.amount}}).
checkDocxTemplate()
Checks every control command and expression in a Word package against a type environment. validateLayers() runs it on file-backed DOCX layers. Each diagnostic names the part and paragraph, such as word/document.xml paragraph 12.
Automatic Formatting
When a form schema is provided, field values are automatically formatted:
// Money fields formatted as "$50,000.00"
// Date fields formatted as "Apr 1, 2024"
// Phone fields formatted as "(555) 123-4567"Custom Formatters
Use a formatter for different locales:
import { renderDocx } from '@paradoc/render/docx'
import { createFormatter } from '@paradoc/format'
const output = await renderDocx({
template: templateBytes,
data,
form,
formatter: createFormatter({ locale: 'de-DE' }),
})
// EUR amounts use German number formatting; currency stays explicit in the value.Formatting failures reject the render with the field path and status. Invalid values and template errors never produce a silently unfinished document. An artifact-level formatter passed to render() overrides the renderer default.
Values in templates
A DOCX template names each value by its path, such as {{fields.clientFullName}}. A DOCX layer takes no bindings or bindingsFrom, and validation rejects them there: bindings belong to PDF layers, where they map AcroForm field names to Paradoc paths.
Related
- Renderers - Renderer interface overview
- Formatting - Value formatting