This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/pdf-generation/from-word-template.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Generate PDFs from a Word template using JavaScript | Nutrient Web SDK

Nutrient Web SDK generates PDF documents from Word templates by merging DOCX files with data. You can run this flow headlessly in the background or in the Nutrient viewer UI.

This guide explains how to:

  • Prepare DOCX templates with placeholders.
  • Populate templates with structured data.
  • Export output as PDF or PDF/A.

General principles

Word templating has three parts:

  • A DOCX file that acts as the template.
  • A template model with placeholder values.
  • Optional delimiter configuration.

Template model

You can define the template model in an external JSON file or build it programmatically.

The model includes:

  • Optional delimiter configuration (start and end). If omitted, the default delimiters are { and }.
  • At least one placeholder-value pair. Placeholder names must match the placeholders in the DOCX template.

Classes, methods, and properties

This guide uses one entry point from the SDK:

Populate a Word template

Nutrient Web SDK supports placeholder replacement, loops, and dynamic tables. When content size changes, the DOCX template reflows across pages. Placeholders keep their original DOCX formatting.

Generating a PDF from a Word Template in Web

Start by preparing a DOCX template with placeholders. The example below uses {{ and }} delimiters.

Placeholder naming rules

Placeholder names must follow these rules:

  • Supported characters — Letters (a–z, A–Z), numbers (0–9), and underscores (_)
  • Valid examples{{name}}, {{firstName}}, {{item_1}}, {{TOTAL_AMOUNT}}
  • Invalid examples{{first-name}}, {{item.price}}, {{user@email}}, {{my placeholder}}

These rules help the parser process templates correctly and avoid conflicts with special syntax characters.

Word Template with placeholders

Prepare data:

const data = {
config: {
delimiter: {
start: "{{",
end: "}}",
},
},
model: {
name: "Alex Smith",
text: "Hello World!",
amount: "$249.99",
},
};

Load the DOCX template and replace placeholders. This returns an ArrayBuffer with the populated DOCX:

const buffer = await NutrientViewer.populateDocumentTemplate(
{
// Other configuration options.
document: "template.docx",
},
data,
);

The following image shows the populated DOCX output.

Word Template populated with JSON data and saved to DOCX

Next, convert the DOCX to PDF. The following example converts to PDF/A-1a. Choose the conformance value you need from the conformance enumeration.

const pdfBuffer = await NutrientViewer.convertToPDF(
{
// Other configuration options.
document: buffer,
},
NutrientViewer.Conformance.PDFA_1A,
);

Image substitution

Along with text, loops, and conditionals, NutrientViewer.populateDocumentTemplate() also supports image substitution in standalone mode.

Use these markers in your DOCX template:

  • {{%name}} — Inserts an inline image
  • {{%%name}} — Inserts a centered image

Set the model entry to an image object with _type: "image":

const data = {
config: {
delimiter: {
start: "{{",
end: "}}",
},
},
model: {
companyName: "Nutrient",
logo: {
_type: "image",
source: "url",
url: "https://example.com/logo.png",
sizing: "fit-width",
width: 96,
altText: "Nutrient logo",
},
},
};

Supported image sources

The source field selects how the image payload is supplied:

  • source: "base64" — Raw Base64 payload in data.
  • source: "dataUrl" — Data URL payload in data (alias of base64).
  • source: "url" — HTTP(S) URL in url (fetched and normalized at runtime).

For URL sources, the host must enable CORS. If CORS isn’t configured, use Base64 or data URL payloads.

Sizing and multipage behavior

The sizing mode determines which dimension fields are required:

  • If you omit sizing, preprocessing keeps sizing unchanged, and the engine applies default layout behavior.
  • sizing: "original" requires no dimensions.
  • sizing: "fixed" and sizing: "fit-max" require both width and height (positive numbers).
  • sizing: "fit-width" requires width (positive number).
  • sizing: "fit-height" requires height (positive number).
  • For multipage images, pageNumber is supported and must be a positive one-based integer.

Validation and limitations

A few constraints to be aware of:

  • For raw Base64 payloads, set format explicitly (for example png, jpg, gif, bmp, tif).
  • source: "file" isn’t supported in browser public APIs.
  • SVG payloads and SVG formats aren’t supported in this release.
  • If any image entry is invalid or can’t be resolved, the complete templating request fails.

Image entries also support optional properties such as borders, captions, rotation, link targets, and accessibility metadata. For the full list, refer to the NutrientViewer.populateDocumentTemplate API reference.

Advanced example: Looping through data sets

Loops repeat content based on arrays in your data model. Use loops for invoice line items, employee lists, or other repeated sections.

If your data contains three items, the loop renders three repeated sections:

lineItems: [
{ product: "Laptop", price: "$999" },
{ product: "Mouse", price: "$25" },
{ product: "Keyboard", price: "$75" },
];

Creating loops

Add an array of objects to the data model. For each object, the loop body is repeated when the engine processes the DOCX and model.

Step one — Prepare your data

const data = {
config: {
delimiter: {
start: "{{",
end: "}}",
},
},
model: {
loop1: [
{ loopDesc1: "Monday", loopDesc2: "Tuesday" },
{ loopDesc1: "Wednesday", loopDesc2: "Thursday" },
{ loopDesc1: "Friday", loopDesc2: "Saturday" },
],
loop2: [{ loopDesc: "Red" }, { loopDesc: "Orange" }, { loopDesc: "Green" }],
},
};

Step two — Create your DOCX template with loop placeholders

The template defines where and how loops repeat content.

Word Template with placeholders for creating loops

For bulleted or numbered lists, place the opening placeholder immediately before the list and the closing placeholder immediately after it.

Step three — Generate the populated document

When you combine the data and template, the loops generate the repeated content.

Populated Word Template with Dynamic Table Rows

For an example that combines loops with tables, refer to the dynamic table loop example section below.

Nested loops example

Nested loops iterate through multilevel data structures, such as categories that contain items. This is useful for documents such as catalogs, menus, and hierarchical reports.

Real-world scenario — Restaurant menu

This example uses multiple sections (starters, entrees, sides), each with multiple items and descriptions.

Step one — Prepare nested data structure

const data = {
config: {
delimiter: {
start: "{{",
end: "}}",
},
},
model: {
title: "New Year’s",
subtitle: "celebration",
sections: [
{
title: "STARTERS",
items: [
{
title: "SALSA TRIO & FRESH CHIPS",
description: "Black Bean Salsa, Green Chili, Pico de Gallo",
hasDescription: true,
},
{
title: "SHRIMP & AVOCADO CEVICHE",
description: "Gulf Shrimp Marinated in Lime with Cilantro",
hasDescription: true,
},
],
},
{
title: "ENTREES",
items: [
{
title: "GRILLED ACHIOTE CHICKEN",
description: "Red Chili & Avocado Puree",
hasDescription: true,
},
{
title: "SHREDDED BEEF ENCHILADAS",
description: "Green Chili & Cheddar",
hasDescription: true,
},
],
},
{
title: "SIDES",
items: [
{
title: "CALABACITAS",
description: "Zucchini, Poblano Corn, Black Beans",
hasDescription: true,
},
{
title: "CILANTRO LIME RICE",
description: null,
hasDescription: false,
},
],
},
],
},
};

Step two — Create your DOCX template with nested loop placeholders

Structure placeholders like this:

{{title}} {{subtitle}}
{{#sections}}
{{title}}
{{#items}}
• {{title}}
{{#hasDescription}}{{description}}{{/hasDescription}}
{{/items}}
{{/sections}}

Loop structure

  • Outer loop{{#sections}}...{{/sections}} iterates through sections.
  • Inner loop{{#items}}...{{/items}} iterates through items in each section.
  • Conditional{{#hasDescription}}...{{/hasDescription}} renders only when the Boolean flag is true.

Step three — Generated output

This structure generates the following output:

New Year’s celebration
STARTERS
• SALSA TRIO & FRESH CHIPS
Black Bean Salsa, Green Chili, Pico de Gallo
• SHRIMP & AVOCADO CEVICHE
Gulf Shrimp Marinated in Lime with Cilantro
ENTREES
• GRILLED ACHIOTE CHICKEN
Red Chili & Avocado Puree
• SHREDDED BEEF ENCHILADAS
Green Chili & Cheddar
SIDES
• CALABACITAS
Zucchini, Poblano Corn, Black Beans
• CILANTRO LIME RICE

Adding conditional logic

Conditionals insert and format content based on conditions in the template. You can combine conditionals with loops.

Use this syntax:

  • #condition — Starts a block rendered when the condition is true.
  • ^condition — Starts a block rendered when the condition is false.
  • /condition — Ends the conditional block.

Example using isBlue:

{{#isBlue}}Blue{{/isBlue}}{{^isBlue}}Red{{/isBlue}}

The following is a more complex conditional example.

Word Template with placeholders for using conditionals

Combine the template above with this data:

{
config: {
delimiter: {
start: "{{",
end: "}}"
}
},
model: {
isConsulting: false,
consultingField: "N/A",
scopeExhibit: "Exhibit A",
startDate: "March 1, 2024",
endDate: "February 28, 2025",
autoRenew: true
}
}

This produces the following DOCX, which you can then convert to PDF.

Word Template with placeholders for using conditionals

Dynamic table loop example

This example uses loops to insert rows into a table dynamically. Download the DOCX template used in this example.

Generating a PDF from a Word Template in Web

Using a model loaded from a JSON file

Populate the DOCX template with data from a JSON file. You can download the JSON file used in this example:

const data = await fetch("table.json").then((response) => response.json());
const docx = await NutrientViewer.populateDocumentTemplate(
{
// Other configuration options.
document: "table.docx",
},
data,
);
const pdfBuffer = await NutrientViewer.convertToPDF(
{
// Other configuration options.
document: docx,
},
NutrientViewer.Conformance.PDFA_1A,
);

Automatic reflow example

Text reflow automatically adjusts text to fit available layout space. The engine moves text to new lines, pages, or columns as content size changes.

Word Template with text reflowing

Use the following JSON:

{
"config": {
"delimiter": {
"start": "{{",
"end": "}}"
}
},
"model": {
"clauses": [
{
"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
},
{
"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
},
{
"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
},
{
"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
},
{
"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
}
],
"title": "Purchase and Sale Agreement"
}
}

The example below generates a PDF from a DOCX template where content reflows into another column using a model loaded from JSON. For a programmatic model example, refer to the dynamic table loop example section above.

data = await fetch("data.json").then((response) => response.json());
const docx = await NutrientViewer.populateDocumentTemplate(
{
// Other configuration options.
document: "reflow.docx",
},
data,
);
const pdfBuffer = await NutrientViewer.convertToPDF(
{
// Other configuration options.
document: docx,
},
NutrientViewer.Conformance.PDFA_1A,
);

Canceling template population

To cancel an in-progress populateDocumentTemplate call, pass an AbortSignal(opens in a new tab) through the signal configuration option. This also works with convertToPDF in the same pipeline:

When the signal is aborted, the operation promise rejects with a DOMException whose name is AbortError. HTTP-based steps (such as document fetch) are canceled at the network level. For WebAssembly/worker-based processing, the promise still rejects immediately, but underlying processing may continue in the background briefly.

const controller = new AbortController();
async function generatePdf() {
const docx = await NutrientViewer.populateDocumentTemplate(
{
document: "template.docx",
signal: controller.signal,
},
data,
);
const pdfBuffer = await NutrientViewer.convertToPDF(
{
document: docx,
signal: controller.signal,
},
NutrientViewer.Conformance.PDFA_1A,
);
return pdfBuffer;
}
generatePdf().catch((error) => {
if (error.name === "AbortError") {
console.log("Generation cancelled");
}
});
// Cancel at any point during the pipeline.
controller.abort();