Mail-merge cannot do 'show this paragraph only if there is a bonus' or 'name each file after the person'. A 60-line Python pattern can.
Word mail-merge is fine for one letter with 40 names. It breaks on the two things office work actually needs:
Both are trivial in Python and impossible in mail-merge without macros.
from docx import Document
import re
def render(template, row):
def sub(m):
kind, field, body = m.group(1), m.group(2), m.group(3)
has = str(row.get(field, "")).strip() not in ("", "None")
if kind == "#": return body if has else ""
if kind == "^": return "" if has else body
# conditional blocks first, repeatedly for nesting
text = re.sub(r"\{\{([#^])([A-Za-z0-9_]+)\}\}(.*?)\{\{/\2\}\}",
sub, template, flags=re.S)
# then plain values
return re.sub(r"\{\{([A-Za-z0-9_]+)\}\}",
lambda m: str(row.get(m.group(1), "")), text)
doc = Document("tpl.docx")
for p in doc.paragraphs: # also loop tables / headers / footers
full = "".join(r.text for r in p.runs)
if "{{" in full:
new = render(full, row)
p.runs[0].text = new # keeps the first run's formatting
for r in p.runs[1:]: r.text = ""
doc.save(f"out/{row['name']}-letter.docx")
Runs split text. Word stores a paragraph as a list of runs with different formatting, so
{{na and me}} can be two runs. Join the paragraph text first, replace, then
write the result back into the first run — otherwise your placeholder never matches.
Formatting the values is the product. 13943 is not an offer letter;
13,943.00 is. Same for 2026-07-22 versus 22 July 2026. Build a
small filter syntax ({{salary|money}}, {{start|date:%d %B %Y}}) rather than
formatting in the data — the template is what people edit.
Excel exports Chinese CSVs as GBK or adds a UTF-8 BOM. Read defensively:
utf-8-sig first, then gbk. If you skip this, half your documents contain
??? and you find out after sending 200 of them.
DocFiller implements all of the above — filters
(upper/title/money/date:/pad:),
conditional sections, images, per-row filenames with subfolders, optional PDF conversion, and a
_manifest.csv audit trail. Demo template and data included, 29 USDT.
One Word template + one spreadsheet = hundreds of finished documents. No mail-merge.
Get it — 29 USDT