Skip to content
This documentation is currently in preview, therefore subject to change.

Author Advanced Word Templates

Template Authoring Guide

This guide covers the advanced template authoring techniques for designing robust, dynamic templates directly inside Microsoft Word.

The Word Template Syntax guide should be used as a companion when creating templates, as Build a Doc template syntax is required to generate dynamic documents.

The Build a Doc Word Add-in is a useful tool designed to enable the user to view what their documents will look like. It is recommended to use the Add-in whilst you are creating your template.

Adding the Build a Doc Word Add-in

To use the Build a Doc Word Add-in for template authoring:

  1. Open Microsoft Word (Desktop or Online)
  2. Click the Home tab → Add-ins
  3. Search for Build a Doc
  4. Click Add to install

The Build a Doc pane will appear on the right side of Word, ready for template validation and preview.


Providing Sample Data for Preview

Once you’ve created your template structure and added tags, you need to provide sample data to preview the results in the Word Add-in. For detailed guidance on structuring your JSON data, see Structure JSON Data.

Using the Add-in Data Pane

To preview your template with sample data:

  1. Open the Build a Doc Add-in pane (right side of Word)
  2. Navigate to the Build tab
  3. In the data input area, paste your JSON data as an array
  4. Click Preview to generate a sample document
  5. Review the generated preview to verify your template tags work correctly

For comprehensive guidance on preparing and testing sample data, see Preview Templates with Sample Data. For guidance on structuring your JSON data correctly, see Structure JSON Data.


The Authoring Workflow

Creating a template involves three main steps: Structure, Tagging, and Logic.

1. Structure Your Document

Before adding any dynamic tags, build the static layout of your document using standard Word features.

  • Tables: Create the headers for your invoices or reports.
  • Styles: Apply your headers, formatting, and standard font styles. The generated document will retain all these stylistic choices.
  • Placeholders: It is often helpful to type dummy text (e.g., [Client Name]) where you intend to put dynamic data later.

2. Inserting Data Tags

After creating the basic structure of the document, replace your placeholder text with real Build a Doc template syntax that matches your JSON Data structure.

Example JSON Data:

[
{
"Name": "Invoice",
"Data": {
"Customer": {
"Name": "John Smith"
},
"Items": [
{
"Description": "Product A",
"Price": 10.0
},
{
"Description": "Product B",
"Price": 15.5
}
]
}
}
]
  • Insert your Basic Fields: To display a simple field, use the path to that data.

    • Example: To show the Customer Name, type: <<[Invoice.Customer.Name]>>

3. Applying Logic and Formatting


Advanced Expressions

Mathematical Operations

Perform calculations directly in templates:

Subtotal: £<<[Quantity * UnitPrice]>>
Tax (20%): £<<[Quantity * UnitPrice * 0.2]>>
Total: £<<[Quantity * UnitPrice * 1.2]>>

String Operations

Manipulate text values:

<<[CustomerName.ToUpper()]>>
<<[Description.Substring(0, 50)]>>...

Date Formatting

Format dates for display:

<<[OrderDate]:"dd MMMM yyyy">> → 22 January 2025
<<[OrderDate]:"dd/MM/yy">> → 22/01/25
<<[OrderDate]:"dddd">> → Wednesday

Variables

Use variables for running totals and calculations:

Declaring Variables

<<var [grandTotal = 0]>>

Updating Variables

Inside loops, accumulate values:

<<foreach [item in Items]>>
<<var [grandTotal = grandTotal + (item.Quantity * item.Price)]>>
<<[item.Description]>> - £<<[item.Quantity * item.Price]>>
<</foreach>>
Grand Total: £<<[grandTotal]>>

Complex Conditionals

Multiple Conditions

<<if [Status == "VIP" && TotalPurchases > 1000]>>
Premium Customer Discount: 15%
<<elseif [TotalPurchases > 500]>>
Loyalty Discount: 10%
<<else>>
Standard Pricing
<</if>>

Conditional Tables

Show or hide entire table rows:

ConditionDescription
<<if [ShowShipping]>>Start conditional row
Content hereRow content
<</if>>End conditional

Nested Loops

Handle hierarchical data:

<<foreach [category in Categories]>>
Category: <<[category.Name]>>
<<foreach [product in category.Products]>>
- <<[product.Name]>>: £<<[product.Price]>>
<</foreach>>
<</foreach>>

Table Techniques

Dynamic Row Expansion

The loop creates one row per item:

ProductQtyPriceTotal
<<foreach [item in Items]>><<[item.Product]>><<[item.Qty]>>£<<[item.Price]>>£<<[item.Qty * item.Price]>><</foreach>>

Conditional Rows

Include rows only when conditions are met:

<<if [IncludeDiscount]>>
| Discount | | | -£<<[DiscountAmount]>> |
<</if>>

Summary Rows

Add totals after dynamic content:

| **Total** | | | **£<<[Items.Sum(i => i.Qty * i.Price)]>>** |

LINQ Expressions

Use LINQ-style expressions for data manipulation:

Sum

<<[Items.Sum(i => i.Price)]>>

Average

<<[Items.Average(i => i.Rating)]>>

Count

<<[Items.Count()]>>
<<[Items.Where(i => i.Status == "Active").Count()]>>

Filter

<<foreach [item in Items.Where(i => i.InStock)]>>

Order By

<<foreach [item in Items.OrderBy(i => i.Name)]>>

Images and Formatting

Preserve Formatting

Template formatting (fonts, colours, styles) is preserved in output.

Dynamic Images

For base64-encoded images:

<<[LogoImage]>>

Word Template Operations

OperationExamples and Notes
All(Predicate)persons.All(p => p.Age < 50)
Any()persons.Any()
Any(Predicate)persons.Any(p => p.Name == "John Smith")
Average(Selector)persons.Average(p => p.Age)

The input selector must return a value of any type that has predefined or user-defined addition and division operators.
Concat(IEnumerable)persons.Concat(otherPersons)

An implicit reference conversion must exist between types of items of concatenated enumerations.
Contains(Object)persons.Contains(otherPersons.First())
Count()persons.Count()
Count(Predicate)persons.Count(p => p.Age > 30)
Distinct()persons.Distinct()
First()persons.First()
First(Predicate)persons.First(p => p.Age > 30)
FirstOrDefault()persons.FirstOrDefault()
FirstOrDefault(Predicate)persons.FirstOrDefault(p => p.Age > 30)
GroupBy(Selector)persons.GroupBy(p => p.Age)

Or:

persons.GroupBy(p => new { Age = p.Age, Count = p.Children.Count() })

Returns an enumeration of group objects. Each group has a unique Key defined by the selector and can be treated as an enumeration of its items.
Last()persons.Last()
Last(Predicate)persons.Last(p => p.Age > 100)
LastOrDefault()persons.LastOrDefault()
LastOrDefault(Predicate)persons.LastOrDefault(p => p.Age > 100)
Max(ComparableSelector)persons.Max(p => p.Age)
Min(ComparableSelector)persons.Min(p => p.Age)
OrderBy(ComparableSelector)persons.OrderBy(p => p.Age)

Or:

persons.OrderBy(p => p.Age).ThenByDescending(p => p.Name)

Or:

persons.OrderBy(p => p.Age).ThenByDescending(p => p.Name).ThenBy(p => p.Children.Count())

Returns an ordered enumeration. Use ThenBy or ThenByDescending to specify additional keys.
OrderByDescending(ComparableSelector)persons.OrderByDescending(p => p.Age)

Or:

persons.OrderByDescending(p => p.Age).ThenByDescending(p => p.Name)

Or:

persons.OrderByDescending(p => p.Age).ThenByDescending(p => p.Name).ThenBy(p => p.Children.Count())
Select(Selector)persons.Select(p => p.Name)
SelectMany(EnumerationSelector)persons.SelectMany(p => p.Children)
Single()persons.Single()
Single(Predicate)persons.Single(p => p.Name == "John Smith")
SingleOrDefault()persons.SingleOrDefault()
SingleOrDefault(Predicate)persons.SingleOrDefault(p => p.Name == "John Smith")
Skip(int)persons.Skip(10)
SkipWhile(Predicate)persons.SkipWhile(p => p.Age < 21)
Sum(Selector)persons.Sum(p => p.Children.Count())

The selector must return a value with a predefined or user-defined addition operator.
Take(int)persons.Take(5)
TakeWhile(Predicate)persons.TakeWhile(p => p.Age < 50)
Union(IEnumerable)persons.Union(otherPersons)

An implicit reference conversion must exist between types of items of united enumerations.
Where(Predicate)persons.Where(p => p.Age > 18)

Validating Your Template

As you author, frequent validation saves time.

  • Use the Preview: The most effective validation is generating a preview.
  • Check Error Logs: If generation fails, the Add-in provides an error log. Look for “Unexpected end of tag” (usually a missing >>) or “Invalid path” (typos in JSON keys).

Best Practices

  1. Keep logic simple - Complex business logic belongs in your data preparation, not templates
  2. Test incrementally - Add complexity gradually, testing at each step
  3. Use meaningful names - Clear variable and data source names improve maintainability
  4. Document templates - Add comments or maintain separate documentation
  5. Version control - Track template versions in SharePoint or version control

Publishing Templates to Production

Once validated, store templates in SharePoint Document Libraries or OneDrive for Business with clear versioning.

/Templates/
├── Production/
│ ├── Invoices/Invoice_v1.0.docx
│ └── Reports/WeeklySalesDigest_v1.0.xlsx
├── Development/
└── Archive/

Version Management

Use TemplateName_vX.Y.docx naming:

  • X = Major version (breaking changes)
  • Y = Minor version (bug fixes)

Never modify templates in place - always create new versions. Keep old versions in Archive for rollback.

Deployment Checklist

Before deploying to production:

  1. Test with representative data including edge cases
  2. Verify data source keys match template expressions (case-sensitive)
  3. Confirm service account has read access to template file
  4. Update flow to reference new template location
  5. Monitor initial runs and have rollback plan ready