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

Excel Template Syntax

Converts an Excel document (.xls, .xlsx) to another format, returning the resulting file.

Excel templates can use a simple templating language and data sources to generate documents. Excel templates look like regular documents, with embedded template expressions.

The example below shows the generated output from an Excel template and a data source:

AB
1Name:<<[Person.FirstName]>> <<[Person.LastName]>>
2Address:
3<<[Person.Address.Number]>>, <<[Person.Address.Street]>>
4<<[Person.Address.City]>>
5<<[Person.Address.Postcode]>>

How to insert data into an Excel Spreadsheet

Step 1: Create an Excel File:

Open Excel and save a new workbook. This is where you will write your template expressions.

Step 2: Define your template region:

In order to insert data from a data source into a spreadsheet, regions must be defined in order for the templating syntax to know where to insert the values.

This can be done either by Creating A Named Range or Formatting as a table, and prefixing its name with a _ (underscore) to mark as a template region, for example _shoppingList.

Step 2a: Creating a Named Range

Creating a Named Range
  1. On the Formulas tab, in the Defined Names group, click Define Name.

  2. In the New Name dialog box, in the Name box, type in the name that you want to use for your reference.

  3. To specify the scope of the named range, in the Scope drop-down list box, select Workbook or the name of a worksheet in the workbook.

    A named range set to a Workbook scope will be available for use throughout the workbook, whereas a range set to a particular sheet’s scope can be used within the sheet only.

  4. Optionally, in the Comment box, enter a descriptive comment up to 255 characters.

  5. In the Refers to box, do one of the following:

    • Click Collapse Dialog (which temporarily shrinks the dialog box), select the cells on the worksheet, and then click Expand Dialog
    • To enter a constant, type = (equals sign) and then the constant value.
    • To enter a formula, type = and then type the formula.
  6. Click OK.

Step 2b: Formatting as a table

Formatting as a Table

Excel provides numerous predefined table styles that you can use to quickly format a table. If the predefined table styles don’t meet your needs, you can create and apply a custom table style. Although you can delete only custom table styles, you can remove any predefined table style so that it is no longer applied to a table.

Excel table

You can further adjust the table formatting by choosing Quick Styles options for table elements, such as Header and Total Rows, First and Last Columns, Banded Rows and Columns, as well as Auto Filtering.

Choose a table style

When you have a data range that is not formatted as a table, Excel will automatically convert it to a table when you select a table style. You can also change the format for an existing table by selecting a different format.

  1. Select any cell within the table, or range of cells you want to format as a table.

  2. On the Home tab, select Format as Table.

Excel Home Tab Format As Table

  1. Select the table style that you want to use.

Step 3: Inserting Templating Syntax

Within your defined template region you can now use templating syntax and data sources according to the target output format.

For example, within a _shoppingList formatted table region, you could:

Item NamePriceQuantityUse By Date
<<foreach [item in shoppingList]>> <<[item.name]>><<[item.price]>><<[item.quantity]>><<[item.useByDate]>> <</foreach>>

Language features

Simple expressions

The following template defines two template expressions. If applied to the input object, the expressions will be replaced by the corresponding properties. An expression is a <<, some contents, followed by a >>. When the template is executed, these expressions are replaced with values from a data source.

A
1<<[FirstName]>> <<[LastName]>>

Nested input objects

Sometimes, the input objects contain other objects or arrays. In such a case, you can use a dot-notation to gain access to the nested properties. For example:

A
1<<[Person.FirstName]>> <<[Person.LastName]>>

Some of the built-in helpers allow you to change the current context to a nested object. You can then access this object as if it were the root object.

Conditionals

You can use the if helper to conditionally render a block. If its argument returns false, undefined, null, "", 0, or [], the block will not be rendered.

A conditional expression must return a Boolean value (‘true’ or ‘false’). If none of the conditional expressions equate to true, and no fallback template is provided, the entire conditional block is removed.

The first expression that returns true will trigger the corresponding template option to be used.

<<if [conditional_expression1]>>
template_option1
<<elseif [conditional_expression2]>>
template_option2
<<else>>
default_template_option
<</if>>

For example:

A
1<<if [Author]>>
2<<[FirstName]>> <<[LastName]>>
3<<else>>
4Unknown author
5<</if>>

If the input is an empty JSON object {}, then Author will become undefined and if condition fails. This will result in the output being:

Unknown author

Operators

Build a Doc supports a number of operators to assist with template creation.

Operators are special symbols or keywords that tell the template engine how to manipulate values: whether it’s accessing data, performing mathematical operations, making decisions, or combining pieces of text. They are the “verbs” of your template expressions: they act on your data to produce the final output.

Primary Operators

Operators that let you access data and call functions. The expressions should be written using this syntax template: <<[x]>>

OperatorDescriptionExample
x.yAccess member/property y on object x.person.Name → “Alice”
x?.ySafely access y on x; returns null if x is null.user?.Emailnull
x.f()Call helper or method f on variable x.name.ToUpper() → “ALICE”
a[x]Retrieve element at index position x from array or list a.colors[0] → “Red”
a?[x]Safely access item at index positon x in a; returns null if a is null.items?[2]null
new T(...)Create a new instance of type T with the given constructor arguments.new DateTime(2025,1,1) → date obj
Unary Operators

Operators that work on a single value. The expressions should be written using this syntax template: <<[-x]>>

OperatorDescriptionExample
-xNegate a number: if amount is 5, then -amount yields -5; if total is -3, then -total yields 3-amount-5
!xLogical NOT: flips a boolean (truefalse)!isActive
~xBitwise NOT: flips each single bit in the number - swaps all the 0s for 1s and all the 1s for 0s. Applies it to 0 (which is all zeros in binary) turns every bit into a 1, producing -1~0-1
(T)xCast value x to type T(int)3.143
Binary Operators
Arithmetic

Operators that perform basic mathematical calculations (addition, subtraction, multiplication, division, remainder). The expressions should be written using this syntax template: <<[x * y]>>

OperatorDescriptionExample
x * yMultiply2 * 36
x / yDivide6 / 23
x % yRemainder7 % 43
x + yAdd numbers or concatenate strings"A" + "B" → “AB”
x - ySubtract5 - 23
Bitwise

Operators that manipulate individual bits of integer values (shift, AND, OR, XOR, NOT). The expressions should be written using this syntax template: <<[x << y]>>

OperatorDescriptionExample
x << yShift bits of x left by y positions1 << 24
x >> yShift bits of x right by y positions4 >> 12
x & yBitwise AND3 & 11
x ^ yBitwise XOR5 ^ 36
x | yBitwise OR5 | 27
Relational

Operators that compare two values and return a boolean (true/false) result. The expressions should be written using this syntax template: <<[x < y]>>

OperatorDescriptionExample
x < yTrue if x is less than y.2 < 3true
x > yTrue if x is greater than y.3 > 2true
x <= yTrue if x is ≤ y.2 <= 2true
x >= yTrue if x is ≥ y.3 >= 4false
x == yTrue if x equals y.5 == 5true
x != yTrue if x does not equal y.5 != 4true
Logical

Operators that combine boolean values to form more complex conditions. The expressions should be written using this syntax template: <<[(x && y)]>>

OperatorDescriptionExample
x && yTrue only if both x and y are true.true && falsefalse
x || yTrue if either x or y is true.true || falsetrue
Null-Coalescing

Operators that provides a default when a value is null. The expressions should be written using this syntax template: <<[(x ?? y)]>>

OperatorDescriptionExample
x ?? yReturn x if not null; otherwise return y.name ?? "Unknown"

Loops

You can iterate over a list using the built-in foreach helper. Inside the block, you can use variable name to reference the element being iterated over.

<<foreach [variable_name in sequence_expression]>>
<<[data_band_body]>>
<</foreach>>

For example:

A
1<<foreach [person in people]>>
2<<[person]>>
3<</foreach>>

Reference item position

When looping through items in a foreach statement, you can reference the position of each item using the IndexOf() and NumberOf() extension methods.

  • IndexOf() returns a zero‑based index (0 for the first item, 1 for the second, etc.).

  • NumberOf() returns a one‑based index (1 for the first item, 2 for the second, etc.).

IndexOf() Example: Comma-Separated List

To manipulate the data source to turn it into a comma-separated list, the below method can be applied, utilising the IndexOf() method:

A
1The fruits are:
2<<foreach [fruit in items]>>
3<<[ IndexOf() > 0 ? ”, ” : "" ]>><<[fruit]>>
4<</foreach>>.

The example uses the foreach loop, applying the ternary rule ‘if the item’s index is greater than 0, add a comma before the item, else do nothing’ to each item from the data source.

  • First pass: IndexOf() = 0 → no comma

  • Later passes: IndexOf() > 0 → insert ", "

This results in a comma-separated list, with a comma after each item except the first.

NumberOf() Example: Simple Numbered List

When looping through items in foreach, you can optionally reference the current loop index via the IndexOf() function.

You can use this function to distinguish sequence items with different indexes and then handle them in different ways. For example, given that items is a list of the strings “apples”, “bananas”, and “oranges”, you can use the following template to enumerate them, prefixing all but the first with commas. This is useful when producing lists.

A
1No. - Item
2<<foreach [item in items]>>
3<<[ NumberOf() ]>> - <<[item]>>
4<</foreach>>

Force move to next Item

You can instruct the engine to force movement to the next item within a loop using a next tag.

This feature is useful in label-print-like scenarios when you need to output data about a fixed number of items in a single table row, like in the following example.

Given that Clients is a list having a field named “Name”, you can use the following template to output three client names per row while outputting names of all clients. The next tag forces an increment in the loop, ‘jumping’ to the next item and allowing access to it before the next pass of the loop. As this moves the count of the loop on one place, the next pass of the loop will continue from the next sequential position.

In this case, the engine produces a document as follows:

A
1<<foreach [c in Clients]>>
2<<[c.Name]>><<next>><<[c.Name]>><<next>><<[c.Name]>>
3<</foreach>>

Formatting values

Expression tags let you control how raw data appears in your document - whether as dates, numbers, or styled text. You insert a format directive directly into your placeholder, which is applied at run time.

You can format values using expression tags. An expression tag serves as a placeholder for an expression result within a template, and allows you to specify a format for the output.

An expression tag has no name and consists of the following elements:

  • An expression enclosed by brackets
  • An optional format string enclosed by double quotes and preceded by the ”:” character
  • An optional html switch
<<[expression]:"format">>
Format string

The format string can be used to format numeric values or strings, and must correspond to the expected format described in the context provided: a string method can only be applied to a string, and a dateTime method can only be applied to a dateTime etc.

If you have a string or numeric value and you want to format it into a specific pattern, you can do so by using the format string within the expression tag. In the below example, the string is formatted to upper case.

A
1<<[string]:upper>>

Dates and Times

Dates from a data source property can be formatted using the following syntax.

<<[expression]:"pattern">>

For example:

A
1<<[person.DateOfBirth]:“dd/MM/yyyy”>>

Numbers

Provides several additional number formats that can not be specified using format strings. The following table describes these formats.

Number FormatDescription
alphabeticFormats an integer number as an upper-case letter (A, B, C, …)
romanFormats an integer number as an upper-case Roman numeral (I, II, III, …)
ordinalAppends an ordinal suffix to an integer number (1st, 2nd, 3rd, …)
ordinalTextConverts an integer number to its ordinal text representation (First, Second, Third, …)
cardinalConverts an integer number to its text representation (One, Two, Three, …)
hexFormats an integer number as hexadecimal (8, 9, A, B, C, D, E, F, 10, 11, …)
arabicDashEncloses an integer number with dashes (- 1 -, - 2 -, - 3 -, …)

The following is an example of how you can use one of these additional number formats, instead of a format string. Given that i is an integer number, you can format i as an upper-case letter (1 = A, 2 = B, 3 = C, …).

Strings

String FormatDescription
lowerConverts a string to lower case (“the string”)
upperConverts a string to upper case (“THE STRING”)
capsCapitalises a first letter of every word in a string (“The String”)
firstCapCapitalises the first letter of the first word in a string (“The string”)

The following is an example of how you can specify an additional string format. Given that s is a string, you can capitalise the first letter of every word in s using the following template.

A
1<<[s]:caps>>

Combine formatters

You can also combine expression formatters like in the following examples. Given that d is a DateTime value, you can convert its textual month representation to upper case using the following template.

A
1<<[d]:“MMMM”:upper>>
A
1<<[i]:roman:lower>>

Using Variables

Templates enable you to use variables in template documents. Use variables to store expensive calculations once and reuse the result throughout your template. This is particularly useful for complex calculations, such as running totals.

You can declare a variable in a template using a var tag:

<<var [variable_type variable_name = variable_value]>>

If you do not specify the type explicitly, it is determined implicitly from the specified variable value.

Each new variable must have a unique identifier. After a variable is declared in a template, its value can be accessed using its name, just like any other variable.

A
1<<var [s = “Hello!”]>><<[s]>>

You can redefine the value of a variable using a var tag against the name of this variable. For example, the following template outputs string “Hello, World!”

A
1<<var [s = “Hello, ”]>><<[s]>><<var [s = “World!”]>><<[s]>>

Variables come with the following restrictions:

  • You can not redefine the type of a variable.
  • Using a var tag, you can not redefine the value of an iteration variable or a data source.

Tables

You can dynamically populate tables using Build a Doc template expressions.

A table‑row body can span one or more rows of a document table. Its band begins at the start of the first occupied row and ends at the close of the last occupied row:

<<foreach ...>>
<</foreach>>

For example, to populate a document table given the data source.

ABC
1<<foreach [c in Contracts]>> <<[c.Clients.Name]>><<[c.Managers.Name]>><<[c.Price]>> <</foreach>>
2Total:<<[Contracts.Sum(c=>c.Price)]>>

AB
1<<foreach [m in Managers]>><<[m.Name]>><<[Contracts.Where(c => c.ManagerID==m.ManagerID).Sum(c => c.Price)]>>
2<<foreach [c in Contracts.Where(c => c.ManagerID==m.ManagerID)]>><<[c.Clients.Name]>><<[c.Price]>><</foreach>><</foreach>>
3Total:<<[Contracts.Sum(c => c.Price)]>>

AB
1<<foreach [m in Managers]>> <<[m.Name]>><<foreach [c in m.Contracts]>> <<[c.Clients.Name]>> <</foreach>> <</foreach>>

Single Column Tables

Single‑column tables are treated differently: when the opening and closing foreach tags are both placed in the same cell, the engine treats that band as a standard data band rather than a table‑row band by default. This displays the content as a basic block of repeated content, similar to a single line of text, rather than as a table row band, which spreads the content across multiple rows. The examples below demonstrate this behaviour.

Example 1: Table-Row Band

Managers Table (Table-Row Band)

Name
<<foreach [m in Managers]>>
<<[m.Name]>>
<</foreach>>

Example 2: Standard Data Band:

Managers List (Standard Data Band)

Managers
<<foreach [m in Managers]>><<[m.Name]>> <</foreach>>

-greedy Switch

The -greedy switch is used to influence how tags are processed, particularly when using nested tags.

When the -greedy switch is applied to a tag, the engine captures as much content as possible for that tag, including any nested tags. This can be particularly useful if you need to ensure that all relevant data is included, even if there are multiple nested instances of the same tag type; the -greedy switch will help ensure that all iterations of the inner tag are captured effectively without being prematurely terminated by the outer tag.

AB
1<<foreach [items] -greedy>>Item: <<[Name]>> <<foreach [subItems]>> Sub-item: <<[SubItemName]>> <</foreach>> <</foreach>>

Merging Cells

You can dynamically merge table cells with the same textual contents using cellMerge tags.

<<cellMerge -horz>>

A horizontal switch is optional. If the switch is present, it denotes a cell merging operation in a horizontal direction. If the switch is missing, it means that a cell merging operation is to be performed in a vertical direction (the default).

For two or more successive table cells to be merged, in either direction, the following requirements must be met:

  • Each of the cells must contain a cellMerge tag, indicating a cell merging operation in the same direction.
  • Each of the cells must not be already merged in another direction.
  • The cells must have equal textual contents (ignoring leading and trailing whitespaces).

Consider the following template:

...
<<cellMerge>><<[value1]>>
...
<<cellMerge>><<[value2]>>

If value1 and value2 have the same value, say “Hello”, table cells containing cellMerge tags are successfully merged during runtime:

...
Hello
...

If value1 and value2 have different values, say “Hello” and “World”, table cells containing cellMerge tags are not merged during runtime:

...
Hello
...
World