Make new .docx files from a Handlebars-like template.
Template syntax
Hello {{name}}, welcome to {{company}}.
{{#each items}}- {{name}} × {{qty}}
{{/each}}
{{#if paid}}Thanks!{{/if}}
{{#unless paid}}Please pay.{{/unless}}{{#each}} wrapping a single <w:tr> repeats the table row.
{{image var}} standalone in a paragraph embeds an image. The assigns
value is %{bytes: binary, format: :png | :jpeg | :gif, width_cm: number, height_cm: number}. Missing values drop the paragraph.
Why DOCX is tricky
Word frequently splits a single placeholder like {{name}} across
multiple <w:r> runs (different fonts, spellcheck markers, revisions).
Before substitution we run a smart-merge pass that stitches such
fragmented placeholders back together. See DocxTmpl.SmartMerge.
Public API
render/2,render!/2— render an in-memory.docxbinary.render_file/3,render_file!/3— convenience wrappers for paths.
Everything under DocxTmpl.* other than this module is internal and
may change without notice.
Summary
Functions
Render template (raw .docx bytes) with assigns.
Raising variant of render/2.
Read a .docx from disk, render it, return the new bytes.
Raising variant of render_file/3.
List every variable name referenced by template.
Types
Functions
@spec render(docx_binary(), assigns()) :: {:ok, docx_binary()} | {:error, term()}
Render template (raw .docx bytes) with assigns.
@spec render!(docx_binary(), assigns()) :: docx_binary()
Raising variant of render/2.
@spec render_file(Path.t(), assigns(), keyword()) :: {:ok, docx_binary()} | {:error, term()}
Read a .docx from disk, render it, return the new bytes.
@spec render_file!(Path.t(), assigns(), keyword()) :: docx_binary()
Raising variant of render_file/3.
@spec variables(docx_binary()) :: {:ok, [String.t()]} | {:error, term()}
List every variable name referenced by template.
Returns a sorted, deduplicated list of identifiers used in {{var}},
{{#if x}}, {{#unless x}}, and {{#each x}} tags — including names
referenced inside block bodies. Dotted paths (a.b) are returned as-is.
Roles (interpolation vs. condition vs. iteration) are intentionally not distinguished here; callers that need that should parse the template themselves.