docx-editor 1.x has shipped. Vue support, i18n, agents.

Migration guide →
TutorialMarch 15, 20264 min read

Docxtemplater Template Preview Plugin for React

A React plugin for previewing docxtemplater DOCX templates with color-coded variable highlighting, loop detection, and an annotation panel.

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:

  1. Inline highlighting so template tags get colored backgrounds in the document text, making {tenant_name} visually distinct from the words around it
  2. Highlight overlay that renders colored rectangles over tags on the page, with hover and selection states
  3. Annotation panel on the side listing every tag, anchored to its position in the document

Each tag type gets its own highlight:

TagTypeWhat it does
{name}VariableReplaced with a value
{#items}Section startStarts a loop or conditional
{/items}Section endCloses the loop or conditional
{^active}InvertedRenders 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-editor
import { 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