---
title: "Fill a Word template in C#"
url: https://recursiveai.org/onedocument/cookbook/fill-word-template/
description: "Fill DOCX placeholders with JSON data to generate personalized Word documents."
version: "1.0.0"
---

Fill DOCX placeholders with JSON data to generate personalized Word documents.

```shell
dotnet add package RecursiveAI.OneDocument.Documents
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

The sample `template.docx` in the zip contains the placeholder line `Hello {{ customer }}!` and a second paragraph, `Order: {{ order }}`.

```csharp
using RecursiveAI.OneDocument;
using RecursiveAI.OneDocument.Documents;
using System.Text.Json;

using var data = JsonDocument.Parse(
    """{"customer":"Ada","order":42}""");
Template.Render("template.docx", data,
    "filled.docx");
```

Writes `filled.docx` with “Hello Ada!” and “Order: 42”; the snippet prints nothing. The input file is in the zip.

Unzip [the example](https://recursiveai.org/onedocument/cookbook/fill-word-template/fill-word-template.zip), then `dotnet run`.

## Full program

`template.docx` is included in the example. It has these two paragraphs:

```text
Hello {{ customer }}!
Order: {{ order }}
```

The JSON data is shown in the snippet.

```csharp
using RecursiveAI.OneDocument;
using RecursiveAI.OneDocument.Documents;
using System.Text.Json;

using var data = JsonDocument.Parse(
    """{"customer":"Ada","order":42}""");
var warnings = Template.Render(
    "template.docx", data, "filled.docx");
Console.WriteLine($"Warnings: {warnings.Count}");
using var doc = Document.Open("filled.docx");
Console.WriteLine(doc.ExtractText().Trim());
```

`dotnet run` prints:

```text
Warnings: 0
Hello Ada!
Order: 42
```

Missing data renders empty and returns a `template.missing-data` warning by default; strict mode throws `TemplateException`.

## Variations

### Fail on missing data

Replace the full program with this example, which deliberately omits `customer`. The template still contains `Hello {{ customer }}!`, so strict mode throws `TemplateException`; the catch prints the exception.

```csharp
using RecursiveAI.OneDocument;
using RecursiveAI.OneDocument.Documents;
using RecursiveAI.OneDocument.Templating;
using System.Text.Json;

// Missing customer: template.docx still asks for it.
using var data = JsonDocument.Parse("""{"order":42}""");
try
{
    Template.Render("template.docx", data, "strict.docx",
        new TemplateOptions { Strict = true });
}
catch (TemplateException error)
{
    Console.WriteLine(error.GetType().Name);
    Console.WriteLine(error.Message);
}
```

Output:

```text
TemplateException
Fatal template.missing-data at /word/document.xml#body/p2@9: No data named 'customer'.
```

## Related recipes

- [Find and replace text in a Word document in C#](https://recursiveai.org/onedocument/cookbook/find-and-replace-in-word/)
- [Convert PowerPoint to PDF in C#](https://recursiveai.org/onedocument/cookbook/convert-pptx-to-pdf/)
- [Mail merge DOCX files in C#](https://recursiveai.org/onedocument/cookbook/mail-merge/)
- [Extract text from DOCX, XLSX and PPTX in C#](https://recursiveai.org/onedocument/cookbook/extract-text-from-office-files/)

## The short answer

Put placeholder tags in a DOCX file, supply JSON data and call `Template.Render(templatePath, data, outputPath)`. OneDocument reads the template, fills the tags and saves a separate document. The example replaces a customer name and order number while leaving the template available for another customer.

This is a tag-based template, not Word mail merge. Use it when you control the document’s placeholder syntax and want expressions or repeating content. If your file already contains Word `MERGEFIELD` fields, use the [mail-merge recipe](https://recursiveai.org/onedocument/cookbook/mail-merge/) instead. Both approaches produce DOCX files, but their markers and data-processing rules are different.

## How do I create a Word document from a template?

Start with an ordinary DOCX containing the two paragraphs shown above. The data object has `customer` and `order` properties. Keeping the data as a `JsonDocument` makes the input explicit and avoids needing a generated model class in this small example. Keep that JSON document alive until rendering finishes.

The template engine reads each paragraph as one unit. A Word run is a piece of text with shared formatting, such as bold or italic; the engine joins these pieces when it reads a tag. A tag may be split across runs because of typing, spell-checking or formatting and still be recognized. It may not span two paragraphs. That difference is important when a placeholder appears visually wrapped: a line wrap is not necessarily a paragraph boundary, but pressing Enter creates a different unit.

Text inserted by a tag takes the run properties where the tag began. Text after the tag keeps its own run properties. Style the start of a placeholder in the template to control the replacement’s formatting. Don’t assume that the last character of a split tag decides the result’s font or emphasis.

The template engine handles the body, headers, footers, footnotes, endnotes, comments, text boxes and content-control content. Document property parts are not templated. Tags in alternative-content fallback branches, shape names and chart text are not read. These boundaries are documented in [Word templating](https://recursiveai.org/onedocument/docs/templating-docx/); they matter if a visible placeholder remains unchanged after the main paragraphs have rendered correctly.

## How do I create a new document based on a template?

Give each output its own path and render again with the next data object. The file-based call opens the source template for each render and saves to the requested output extension. A `.dotx` template can also render to `.docx`; saving with the DOCX extension writes the document content type. You don’t need to rename archive members by hand.

For a production template, decide how missing values should behave. The default is lenient: missing data prints empty and returns a located warning. An expression that evaluates to an error prints its error value and returns a warning. The full program reports the number of warnings. For a required customer name or invoice number, strict mode is usually the clearer choice: it throws `TemplateException` on missing data or an expression error rather than silently producing an incomplete letter.

The [template engine](https://recursiveai.org/onedocument/docs/templating/) also supports conditions, loops and formula expressions. Those are evaluated by the template engine, not by compiling C# embedded in the document. Use those features when the structure genuinely varies with the data; use simple substitutions for a fixed two-line letter. Cached field results are copied, not recalculated, so a filled template containing page or other fields is not a request to update every Word field. Review the resulting document in its intended viewer before sending it.
