Fill Word templates from CSV or JSON data — one document per row, all in your browser
No template handy? Download the sample offer-letter template (8 KB, contains 9 tags + a table loop) and try it with the sample data below.
| Syntax | What it does | Example |
|---|---|---|
{{tag}} | Replaced by the matching data value | Dear {{name}}, → Dear Jane Doe, |
{{#list}} … {{/list}} | Repeats the enclosed block (a table row, a paragraph) once per array item | 2 bonus rows → the row appears twice |
{{#flag}} … {{/flag}} | Conditional: shows the block only when the value is truthy | {{#vip}}priority support line{{/vip}} |
{{a.b}} | Dots reach into nested objects | {{manager.name}} with JSON data |
| missing tag | Replaced by an empty string (watch the column mapping!) | salary column misspelled → blank |
| Headers & footers | Merge with the same tags as the body | CONFIDENTIAL — {{name}} |
| Word MERGEFIELD | Not read — retype fields as plain {{tags}} | Insert > Quick Parts > Field won't work |
Verified against the merge engine itself: a template with 11 tag tokens (9 simple, one loop pair) produced zero leftover tags in every output, the loop row repeated exactly once per array item, a truthy conditional rendered, and a missing key vanished. Tag names are case-sensitive.
Mail merge is an old idea with clunky tools. Word's version wants a data source wired through field codes; the online services want your spreadsheet uploaded. A .docx is just a ZIP of XML, though, and filling placeholders in it turns out to be a job a browser tab can do completely on its own.
This tool uses easy-template-x (MIT, actively maintained) with JSZip. When you hit Merge, the template is unzipped in memory, every {{tag}} in the body, headers and footers is located — including tags that Word's XML run-splitting scatters across formatting boundaries, which is the part that breaks naive find-and-replace — and replaced with your data. Loop and conditional blocks are handled structurally, repeating table rows as needed, then the package is rebuilt. First use downloads about 250 KB of JavaScript; after that it's cached and works offline.
{{name}}, {{salary}}, {{date}}. Save as .docx.Take an offer-letter template with nine simple tags and a bonus-schedule table row wrapped in {{#bonuses}}…{{/bonuses}} — 8,092 bytes on disk, 11 tag tokens in total. Merging three complete records (company, signer title and bonus milestones included) produced documents of 8,188, 8,191 and 8,202 bytes, delivered in a 19,832-byte ZIP. Run the same template on the sample CSV instead — its rows carry only the five basic columns — and the letters come out at 8,085 to 8,086 bytes with a 19,511-byte ZIP, because the missing tags merge to blank. The interesting one is the third record: Ana's data carried three bonus milestones instead of two, and the table grew from 3 rows (header + 2) to 4 rows (header + 3) automatically while the other two letters kept 3. Same template, per-record expansion — that's the difference between a real merge engine and sed.
After merging, the natural next steps: convert finished letters to PDF with Word to PDF, and strip the author metadata the templates picked up in Word with Document Metadata Remover before sending them out.
Type your placeholders directly into a Word document as {{name}}, {{address}} and so on (any .docx works, including from Google Docs or LibreOffice), put your recipient data in a CSV, and run both through this tool. Each CSV row becomes one filled-in .docx you can download. No Word installation, no Microsoft account, and nothing is uploaded — the merge runs in your browser.
No, and the difference matters. Word's Insert > Quick Parts > Field MERGEFIELD creates special field codes this tool does not read. The fix is one minute of work: replace each field with plain text like {{FirstName}} in the template. The tag format is more portable anyway — the same template works in any tool that reads double-brace tags, and Word never needs to be involved again.
Yes. Put {{#items}} in the first cell of a table row and {{/items}} in the last cell, use {{field}} tags in the cells between, and pass an array of objects as that tag's data. The row repeats once per item. In testing, a 2-row bonus-schedule table expanded to 4 rows for a record with 3 bonus milestones, and headers and footers merge with the same tags as the body.
There is no server-side limit because there is no server. The practical ceiling is your browser's memory: a few hundred rows of letters (roughly 8 KB each) is comfortable on any machine, and everything arrives as one ZIP. Very large runs with huge templates can hit browser memory limits before anything else fails.
A tag with no matching column is replaced with an empty string by default, so the letter still generates — check the column mapping table before merging, because a silent blank in a salary field is worth catching. CSV column names must match tag names exactly (case-sensitive; Company and company are different tags).
No. The template is unzipped, the tags are replaced, and the document is rebuilt entirely inside your browser tab using easy-template-x and JSZip. Customer lists, salaries and addresses never touch a server, which is the main argument for a local mail merge over the online services that ask you to upload your spreadsheet.