0.x postThis post uses the 0.x package names and APIs. For the current release see the 1.x docs and the migration guide.
Template tag visibility
A paralegal opens a 20-page lease agreement in Word. Somewhere in there are {tenant_name}, {lease_start_date}, {monthly_rent}, and maybe 40 other placeholders. They all look like regular text. Nothing distinguishes {landlord_address} from the boilerplate around it.
In a typical docxtemplater workflow, you author a .docx template and place {variables} throughout the document. If a tag is misspelled, such as {recipient_name} written incorrectly, the templating step can leave the bad tag in the output.
For a one-page letter, manual review is usually enough. For a commercial lease with nested line items, conditional clauses, and variables in headers and footers, reviewers need a list of all tags and where they appear.
The template plugin makes tags visible: highlighted inline, color-coded by type, with an annotation panel that lists the template structure.
Here is the plugin on a lease agreement template. Every {variable} is highlighted inline, and the annotation panel on the right lists the tags. Try scrolling through the document, clicking tags in the panel, or uploading your own template:
What it does
The plugin adds three things to the editor:
- Inline highlighting so template tags get colored backgrounds in the document text, making
{tenant_name}visually distinct from the words around it - Highlight overlay that renders colored rectangles over tags on the page, with hover and selection states
- Annotation panel on the side listing every tag, anchored to its position in the document
Each tag type gets its own highlight:
| Tag | Type | What it does |
|---|---|---|
{name} | Variable | Replaced with a value |
{#items} | Section start | Starts a loop or conditional |
{/items} | Section end | Closes the loop or conditional |
{^active} | Inverted | Renders when value is falsy |
In a lease agreement, you'd see amber highlights on {tenant_name}, {lease_start_date}, {monthly_rent}, and blue highlights wrapping {#additional_provisions}...{/additional_provisions} where optional clauses get looped in.
Setup
npm install @eigenpal/docx-js-editorimport { DocxEditor, templatePlugin } from "@eigenpal/docx-js-editor";
function TemplatePreview({ buffer }: { buffer: ArrayBuffer }) {
return (
<DocxEditor
documentBuffer={buffer}
plugins={[templatePlugin]}
/>
);
}Upload a DOCX with {variables} in it, and the plugin scans the document for supported docxtemplater tags. No tag registry is required.
If you want to control the panel:
import { DocxEditor, createTemplatePlugin } from "@eigenpal/docx-js-editor";
const plugin = createTemplatePlugin({
defaultCollapsed: false,
panelPosition: "right",
panelWidth: 280,
});The panel is resizable (200 to 400px) and collapsible.
How detection works
The plugin scans every text node in the document and picks up all standard docxtemplater syntax:
{variable}for simple variables{nested.property}for dot-notation access (useful for things like{tenant.address.city}){#section}/{/section}for loops or conditionals{^inverted}for inverted conditionals (renders when the value is falsy)
The prefix character determines the tag type. No prefix means it's a plain variable.
Section nesting
The parser tracks a section stack. When it hits {#line_items}, it pushes onto the stack. Any variables found inside, like {description}, {quantity}, {unit_price}, get marked as insideSection and added to the parent's nestedVars array:
if (type === 'sectionStart' || type === 'invertedStart') {
tag.nestedVars = [];
sectionStack.push(tag);
} else if (type === 'variable' && sectionStack.length > 0) {
const section = sectionStack[sectionStack.length - 1];
section.nestedVars?.push(name);
tag.insideSection = true;
}This is how the annotation panel knows to show nested variables under a section chip. The {#line_items} chip shows description, quantity, unit_price as sub-items.
The annotation panel
The panel renders chips anchored to each tag's vertical position in the document. Variables get a colored dot. Structural tags get a typed badge like LOOP / IF or IF NOT. Section chips expand to show their nested variables.
Clicking a chip scrolls the editor to that tag and selects it. Hovering a chip highlights the corresponding tag in the document, and vice versa.
For a paralegal reviewing a template, this means they can scan the panel to see every variable in the document without scrolling through 20 pages. If {guarantor_name} shows up and it shouldn't be there, they spot it in the panel and click to jump to it.
Filling templates
The preview plugin shows you where the tags are. To substitute values, use processTemplate:
import { processTemplate } from "@eigenpal/docx-js-editor";
const filledBuffer = processTemplate(templateBuffer, {
tenant_name: "Jane Smith",
lease_start_date: "April 1, 2026",
monthly_rent: "2,400",
landlord_name: "Acme Properties LLC",
});If you need to know what got filled and what didn't:
import { processTemplateDetailed } from "@eigenpal/docx-js-editor";
const result = processTemplateDetailed(templateBuffer, values, {
nullGetter: "keep", // "keep" leaves unfilled tags as-is, "empty" removes them
linebreaks: true,
});
console.log(result.replacedVariables); // ["tenant_name", "lease_start_date", ...]
console.log(result.unreplacedVariables); // ["guarantor_name"]Validation
You can check for syntax errors before processing, things like unclosed tags or malformed braces:
import { validateTemplate, getMissingVariables } from "@eigenpal/docx-js-editor";
const validation = validateTemplate(templateBuffer);
if (!validation.valid) {
console.error(validation.errors);
// [{ message: "Unclosed tag: provisions", type: "parse" }]
}
const missing = getMissingVariables(validation.tags, {
tenant_name: "Jane Smith",
// lease_start_date is missing
});
// ["lease_start_date"]For legal documents, validate tags before generating the final document so malformed sections like {#provisions} do not reach the recipient.
Scanning headers, footers, and footnotes
The editor plugin highlights tags in the visible body. If you also need to scan headers, footers, footnotes, and text boxes (common in legal templates where the firm name is in the header or a matter ID is in the footer), use detectVariablesDetailed:
import { detectVariablesDetailed } from "@eigenpal/docx-js-editor";
const result = detectVariablesDetailed(document);
console.log(result.variables); // ["amount", "client_name", "date", ...]
console.log(result.totalOccurrences); // 15
console.log(result.byLocation);
// {
// body: ["tenant_name", "lease_start_date", "monthly_rent"],
// headers: ["firm_name", "matter_id"],
// footers: ["firm_name"],
// footnotes: [],
// endnotes: [],
// textBoxes: [],
// }Next steps
- Try the live editor demo with a template document
- Read about building a template system for the fill-and-generate workflow
- See the React integration guide if you're starting from scratch
- Browse the source on GitHub