Skip to content

Frequently Asked Questions (FAQ)

Common questions and answers about using Templify for Word document templating.


Table of Contents


Getting Started

Q: What is Templify?

A: Templify is a modern .NET library for generating Word documents from templates. You create a Word template with placeholders like {{Name}}, and Templify replaces them with your data. It supports conditionals, loops, and nested data structures without requiring Microsoft Word to be installed.

Q: What are the system requirements?

A: - .NET 6.0 or later (supports .NET 6.0, 8.0, and 9.0) - DocumentFormat.OpenXml 3.3.0 (automatically installed via NuGet) - Any platform supported by .NET (Windows, macOS, Linux) - No Microsoft Word installation required

Q: How do I install Templify?

A: Via NuGet:

dotnet add package TriasDev.Templify

Or in Visual Studio: Install-Package TriasDev.Templify

Q: Where should I start?

A: Follow this learning path: 1. Quick Start Guide (5 minutes) 2. Tutorial 1: Hello World (30 min) 3. Tutorial 2: Invoice Generator (1 hour) 4. Full API Documentation

Q: Do I need Microsoft Word installed?

A: No! Templify uses the OpenXML SDK to work directly with .docx files. It runs on any platform without Office installed.

Q: Can I use Templify in web applications?

A: Yes! Templify works perfectly in: - ASP.NET Core web APIs - Blazor applications - Azure Functions - Background job processors - Desktop applications - Console applications


Features & Capabilities

Q: What features does Templify support?

A: Core features: - ✅ Placeholders: {{Name}}, {{User.Email}} - ✅ Nested data: {{Company.Address.City}} - ✅ Array indexing: {{Items[0].Name}} - ✅ Boolean format specifiers: {{IsActive:checkbox}}, {{IsVerified:yesno}} - ✅ Boolean expressions: {{(Age >= 18 and HasLicense):yesno}} - ✅ Conditionals: {{#if IsActive}}...{{#else}}...{{/if}} - ✅ Loops: {{#foreach Items}}...{{/foreach}} - ✅ Nested loops: Loops inside loops (arbitrary depth) - ✅ Table row loops: Dynamic table generation - ✅ Loop variables: @index, @first, @last, @count - ✅ Formatting preservation: Bold, italic, colors, fonts maintained - ✅ Comparison operators: >, <, >=, <=, ==, != - ✅ Logical operators: and, or, not - ✅ Localization: Format specifiers adapt to cultures (en, de, fr, es, it, pt) - ✅ JSON support: Use JSON data instead of C# dictionaries - ✅ Type coercion: Automatic number/date conversions

Q: What is NOT supported?

A: Current limitations: - ❌ Mathematical expressions in templates: Can't do {{Price * Quantity}} (calculate in code) - ❌ String manipulation in templates: Can't do {{Name.ToUpper()}} (format in code) - ❌ Image insertion: No direct image placeholders yet - ❌ Chart/graph generation: Not supported - ❌ Macro execution: Security limitation - ❌ Form fields: Use placeholders instead - ❌ Complex formatting changes: Background colors, page breaks (use template structure)

Workaround: Calculate/format values in your C# code before passing to Templify.

Q: Can Templify handle large documents?

A: Yes! Templify is designed for performance: - Processes 1,000+ placeholders in ~100ms - Handles documents with hundreds of pages - Low memory footprint - See Performance Benchmarks

Q: Does Templify support tables?

A: Yes! Full table support: - Replace placeholders in table cells - Loop over table rows: {{#foreach Items}} - Nested tables - Conditional rows - Formatting preservation

Q: Can I use Templify for mail merge?

A: Absolutely! Common mail merge scenario:

var recipients = GetRecipients(); // List of people

foreach (var recipient in recipients)
{
    var data = new Dictionary<string, object>
    {
        ["Name"] = recipient.Name,
        ["Address"] = recipient.Address,
        ["City"] = recipient.City
    };

    using var templateStream = File.OpenRead("letter-template.docx");
    using var outputStream = File.Create($"letter-{recipient.Id}.docx");

    processor.ProcessTemplate(templateStream, outputStream, data);
}

Syntax & Usage

Q: What syntax do I use for placeholders?

A: Use double curly braces: - Simple: {{Name}} - Nested: {{User.Email}} - Array: {{Items[0]}} - With spaces: {{ Name }} (spaces ignored)

Rules: - Letters, numbers, underscore, dot, brackets only - Case-sensitive - No special characters in variable names

Q: How do I access nested properties?

A: Use dot notation:

Data:

var data = new Dictionary<string, object>
{
    ["Company"] = new
    {
        Name = "Acme Corp",
        Address = new
        {
            Street = "123 Main St",
            City = "Springfield"
        }
    }
};

Template:

Company: {{Company.Name}}
Location: {{Company.Address.City}}

Q: How do conditionals work?

A: Use {{#if}}...{{#else}}...{{/if}}:

{{#if IsActive}}
This customer is active.
{{#else}}
This customer is inactive.
{{/if}}

Supported conditions: - Boolean: {{#if IsActive}} - Comparison: {{#if Age > 18}} - Null check: {{#if Email}} (true if not null/empty) - Combined: {{#if Age >= 18 && HasLicense}}

Q: How do loops work?

A: Use {{#foreach}}...{{/foreach}}:

Template:

{{#foreach Items}}
- {{Name}}: {{Price}} EUR
{{/foreach}}

Data:

["Items"] = new List<object>
{
    new { Name = "Product A", Price = 10.00m },
    new { Name = "Product B", Price = 20.00m }
}

Output:

- Product A: 10.00 EUR
- Product B: 20.00 EUR

Q: What are loop special variables?

A: Inside loops, use these: - {{@index}} - Current position (0-based) - {{@first}} - True for first item - {{@last}} - True for last item - {{@count}} - Total number of items

Example:

{{#foreach Items}}
Item {{@index}}: {{Name}}{{#if @last}} (last){{/if}}
{{/foreach}}

Q: Can I nest loops?

A: Yes! Unlimited nesting:

{{#foreach Orders}}
Order #{{OrderId}}:
  {{#foreach Items}}
  - {{Product}}: {{Quantity}} x {{Price}}
  {{/foreach}}
{{/foreach}}

Q: How do I use loops in tables?

A: Place loop markers in table rows:

Product Quantity Price
{{#foreach Items}}
{{Name}} {{Quantity}} {{Price}}
{{/foreach}}

Templify will repeat the row for each item.

Q: How do I handle missing variables?

A: Check ProcessingResult:

var result = processor.ProcessTemplate(templateStream, outputStream, data);

if (result.MissingVariables.Any())
{
    Console.WriteLine("Warning - variables not found:");
    foreach (var variable in result.MissingVariables)
    {
        Console.WriteLine($"  - {variable}");
    }
}

Missing variables are left as-is: {{MissingVar}} remains in output.

Q: How do I format boolean values as checkboxes or Yes/No?

A: Use format specifiers with the :format syntax:

Template:

Task complete: {{IsCompleted:checkbox}}
Approved: {{IsApproved:yesno}}
Valid: {{IsValid:checkmark}}

C# Data:

var data = new Dictionary<string, object>
{
    ["IsCompleted"] = true,
    ["IsApproved"] = false,
    ["IsValid"] = true
};

JSON Data:

{
  "IsCompleted": true,
  "IsApproved": false,
  "IsValid": true
}

Output:

Task complete: ☑
Approved: No
Valid: ✓

Available formatters: - checkbox → ☑/☐ - yesno → Yes/No - checkmark → ✓/✗ - truefalse → True/False - onoff → On/Off - enabled → Enabled/Disabled - active → Active/Inactive

See the Format Specifiers Guide for complete documentation.

Q: Can I use JSON instead of C# dictionaries?

A: Yes! While Templify processes C# Dictionary<string, object> internally, JSON can be easily converted:

JSON Data File (data.json):

{
  "CompanyName": "Acme Corp",
  "IsActive": true,
  "Items": [
    { "Name": "Product A", "Price": 10.00 },
    { "Name": "Product B", "Price": 20.00 }
  ]
}

C# Code:

using System.Text.Json;

// Read and deserialize JSON
string jsonText = File.ReadAllText("data.json");
var data = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonText);

// Process template
var processor = new DocumentTemplateProcessor();
processor.ProcessTemplate(templateStream, outputStream, data);

JSON is particularly useful for: - Business users providing data - API integrations - Configuration files - Testing with sample data

Q: How do boolean expressions work in placeholders?

A: Use parentheses to evaluate logic directly in placeholders:

Template:

Eligible: {{(Age >= 18):yesno}}
Access granted: {{(IsActive and IsVerified):checkbox}}
Can proceed: {{(HasPermissionA or HasPermissionB):checkmark}}

Data:

{
  "Age": 25,
  "IsActive": true,
  "IsVerified": true,
  "HasPermissionA": false,
  "HasPermissionB": true
}

Output:

Eligible: Yes
Access granted: ☑
Can proceed: ✓

Supported operators: - Logical: and, or, not - Comparison: ==, !=, >, >=, <, <= - Nested: ((var1 or var2) and var3)

See the Boolean Expressions Guide for complete documentation.

Q: Can I combine expressions with format specifiers?

A: Yes! This is one of the most powerful features:

Template:

Qualified driver: {{((Age >= 18) and (HasLicense or HasPermit)):yesno}}

Data:

{
  "Age": 20,
  "HasLicense": false,
  "HasPermit": true
}

Output:

Qualified driver: Yes

More examples:

Over budget: {{(Spent > Budget):checkbox}}
Status OK: {{(not HasErrors):checkmark}}
Premium member: {{(MembershipLevel >= 3):enabled}}

Q: How do I customize boolean formatters?

A: Register custom formatters with the registry:

var registry = new BooleanFormatterRegistry();
registry.Register("thumbs", new BooleanFormatter("👍", "👎"));
registry.Register("traffic", new BooleanFormatter("🟢", "🔴"));

var options = new PlaceholderReplacementOptions
{
    BooleanFormatterRegistry = registry
};
var processor = new DocumentTemplateProcessor(options);

Template:

User feedback: {{IsPositive:thumbs}}
System status: {{IsOperational:traffic}}

Output:

User feedback: 👍
System status: 🟢

Q: Do format specifiers work with localization?

A: Yes! Format specifiers automatically adapt to the culture:

German Output:

var options = new PlaceholderReplacementOptions
{
    Culture = new CultureInfo("de-DE"),
    BooleanFormatterRegistry = new BooleanFormatterRegistry(new CultureInfo("de-DE"))
};
var processor = new DocumentTemplateProcessor(options);

Template: {{IsActive:yesno}} Output (de-DE): Ja or Nein Output (fr-FR): Oui or Non Output (es-ES): or No

Supported languages for yesno: - English (en): Yes/No - German (de): Ja/Nein - French (fr): Oui/Non - Spanish (es): Sí/No - Italian (it): Sì/No - Portuguese (pt): Sim/Não

Symbol-based formatters (checkbox, checkmark) are universal.


Performance

Q: How fast is Templify?

A: Very fast! Benchmark results: - Simple replacement: ~0.5ms for 10 placeholders - Complex document: ~100ms for 1,000 placeholders - Table with loops: ~50ms for 100 rows

See detailed Performance Benchmarks.

Q: Does Templify cache templates?

A: No internal caching. Best practice: - For repeated processing, reuse the DocumentTemplateProcessor instance - Cache template streams in your application if needed - Process templates asynchronously for better throughput

Q: Can I process templates in parallel?

A: Yes! DocumentTemplateProcessor is thread-safe for reading. Best practice:

var processor = new DocumentTemplateProcessor();

Parallel.ForEach(dataList, data =>
{
    using var templateStream = GetTemplateStream(); // Separate stream per thread
    using var outputStream = GetOutputStream();

    processor.ProcessTemplate(templateStream, outputStream, data);
});

Important: Each thread needs its own template/output streams.

Q: How can I optimize performance?

A: Tips: 1. Reuse processor: Create once, use many times 2. Minimize template complexity: Fewer loops = faster 3. Pre-calculate values: Don't use complex nested paths 4. Use memory streams: Faster than file I/O 5. Batch processing: Process multiple templates in parallel


Troubleshooting

Q: Why aren't my placeholders being replaced?

A: Common causes: 1. Typo in placeholder name: {{Nmae}} vs {{Name}} (case-sensitive!) 2. Data not provided: Check result.MissingVariables 3. Wrong data structure: Verify nested paths match your object 4. Word formatting broke placeholder: Word sometimes splits {{Name}} into multiple runs

Fix #4: Select the placeholder in Word and clear formatting (Ctrl+Space), then retype it.

Q: Why do I get "Placeholder split across runs" warning?

A: Word's internal format sometimes splits text. Fix: 1. Select the placeholder in Word 2. Press Ctrl+Space (remove formatting) 3. Retype the placeholder without formatting changes mid-text

Q: My conditional isn't working. What's wrong?

A: Check: 1. Condition syntax: {{#if IsActive}} not {{if IsActive}} 2. Variable exists: Provide the variable in data 3. Type mismatch: "true" (string) is not true (boolean) 4. Comparison operators: Use == for equality, not = 5. Closing tag: Must have {{/if}}

Q: Loop isn't repeating. Why?

A: Checklist: 1. Collection exists: Verify data contains the collection 2. Collection is enumerable: Use List<T>, T[], or IEnumerable<T> 3. Closing tag: Must have {{/foreach}} 4. Correct variable name: Case-sensitive!

Q: Why is my document corrupted after processing?

A: Common causes: 1. Stream not disposed: Use using statements 2. Stream position not reset: Call stream.Position = 0 before processing 3. Concurrent access: Don't share streams between threads 4. Template already corrupted: Validate template with Converter tool

Validate template:

dotnet run --project TriasDev.Templify.Converter -- validate template.docx

Q: How do I debug template issues?

A: Steps: 1. Check ProcessingResult:

if (!result.IsSuccessful)
{
    foreach (var error in result.Errors)
        Console.WriteLine(error);
}

  1. Review missing variables:

    foreach (var missing in result.MissingVariables)
        Console.WriteLine($"Missing: {missing}");
    

  2. Simplify template: Remove complexity until it works, then add back

  3. Check template structure: Use Converter's analyze command:

    dotnet run --project TriasDev.Templify.Converter -- analyze template.docx
    

Q: Can I see what Templify found in my template?

A: Yes! Use the Converter tool:

dotnet run --project TriasDev.Templify.Converter -- analyze template.docx --output report.md

Shows all placeholders, conditionals, and loops found.


Migration

Q: I'm using OpenXMLTemplates. How do I migrate?

A: Templify has a built-in converter!

Step 1: Analyze your template:

./scripts/analyze.sh old-template.docx

Step 2: Convert:

./scripts/convert.sh old-template.docx

Step 3: Update code:

// Old: OpenXMLTemplates
var processor = new TemplateProcessor("template.docx");
processor.SetValue("variable_Name", "John");

// New: Templify
var processor = new DocumentTemplateProcessor();
var data = new Dictionary<string, object> { ["Name"] = "John" };
processor.ProcessTemplate(templateStream, outputStream, data);

See full Converter Documentation.

Q: What's the mapping from OpenXMLTemplates?

OpenXMLTemplates Templify
variable_Name {{Name}}
conditionalRemove_IsActive {{#if IsActive}}...{{/if}}
conditionalRemove_Count_gt_0 {{#if Count > 0}}...{{/if}}
repeating_Items {{#foreach Items}}...{{/foreach}}

Q: Can I migrate from manual OpenXML code?

A: Yes! Benefits: - 90% less code: Typical reduction - No XML knowledge needed: Work with Word templates - Easier maintenance: Templates updated by non-developers - Fewer bugs: No manual XML manipulation

Before (manual OpenXML):

// 50+ lines of XML manipulation
using (var doc = WordprocessingDocument.Open(...))
{
    var body = doc.MainDocumentPart.Document.Body;
    foreach (var text in body.Descendants<Text>())
    {
        if (text.Text.Contains("{{Name}}"))
        {
            text.Text = text.Text.Replace("{{Name}}", name);
        }
    }
    // ... many more lines
}

After (Templify):

// 3 lines!
var data = new Dictionary<string, object> { ["Name"] = name };
processor.ProcessTemplate(templateStream, outputStream, data);


Enterprise & Production

Q: Is Templify production-ready?

A: Yes! Templify is battle-tested in production environments, processing thousands of documents daily.

Quality indicators: - ✅ 109 tests, 100% code coverage - ✅ Battle-tested in enterprise environment - ✅ Comprehensive error handling - ✅ Performance optimized - ✅ Well-documented

Q: What about security?

A: Templify is designed with security in mind: - ✅ No code execution: Templates are data, not code - ✅ No external dependencies: Only OpenXML SDK - ✅ Input validation: Validates template structure - ✅ No macro execution: Security by design - ✅ Safe XML processing: Protection against XXE attacks - ✅ Memory limits: Protection against zip bombs

Best practices: - Validate user-uploaded templates before processing - Sanitize user input before passing to templates - Set resource limits for large-scale processing - Keep OpenXML SDK updated

Q: What's the license?

A: [License information to be added] - See LICENSE file in repository.

Q: Is there commercial support available?

A: Templify is maintained by TriasDev GmbH & Co. KG. For enterprise support: - 📧 Contact: [contact information to be added] - 💬 Community support: GitHub Discussions - 🐛 Bug reports: GitHub Issues

Q: Can I use Templify in commercial applications?

A: Yes! Templify can be used in commercial applications. Check the LICENSE file for specific terms.

Q: How do I handle errors in production?

A: Robust error handling:

try
{
    var result = processor.ProcessTemplate(templateStream, outputStream, data);

    if (!result.IsSuccessful)
    {
        // Log errors for investigation
        _logger.LogError("Template processing failed: {Errors}",
            string.Join(", ", result.Errors));

        // Optionally notify user
        return BadRequest("Document generation failed");
    }

    if (result.MissingVariables.Any())
    {
        // Log warnings
        _logger.LogWarning("Missing template variables: {Variables}",
            string.Join(", ", result.MissingVariables));
    }

    return File(outputStream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
}
catch (Exception ex)
{
    _logger.LogError(ex, "Unexpected error processing template");
    return StatusCode(500, "Internal server error");
}

Q: How do I monitor performance in production?

A: Track these metrics:

var sw = Stopwatch.StartNew();
var result = processor.ProcessTemplate(templateStream, outputStream, data);
sw.Stop();

_logger.LogInformation(
    "Template processed: {Duration}ms, Placeholders: {Count}, Size: {Size}KB",
    sw.ElapsedMilliseconds,
    result.PlaceholdersReplaced,
    outputStream.Length / 1024
);

// Track in your monitoring system (Application Insights, Prometheus, etc.)
_metrics.RecordDuration("templify.processing", sw.ElapsedMilliseconds);
_metrics.RecordCount("templify.placeholders", result.PlaceholdersReplaced);

Comparison

Q: Why use Templify instead of manual OpenXML?

A: Advantages:

Aspect Manual OpenXML Templify
Code complexity High (50-200 lines) Low (5-10 lines)
Learning curve Steep (XML knowledge) Gentle (just placeholders)
Template creation Programmatic Visual (in Word)
Maintenance Difficult Easy
Non-developer friendly No Yes
Error-prone High Low
Performance Similar Optimized

Q: Templify vs DocX library?

A:

Feature Templify DocX
Focus Template processing Document creation
Use case Fill templates Build docs from scratch
Conditionals ✅ Built-in ❌ Code only
Loops ✅ Built-in ❌ Code only
Template syntax ✅ Simple {{}} ❌ N/A
Learning curve Low Medium

Choose Templify when: You have Word templates to fill Choose DocX when: You're building documents programmatically from scratch

Q: Templify vs XSLT templating?

A:

Aspect Templify XSLT
Template format Word .docx XML
Readability High (visual) Low (code-like)
Designer-friendly Yes No
Complexity Simple Complex
Performance Fast Slower
Ecosystem .NET Cross-platform

Q: When should I NOT use Templify?

A: Use alternatives when: - Generating PDFs directly: Use PDF library (e.g., iText) - Complex layouts from scratch: Use DocX or direct OpenXML - Real-time collaborative editing: Use Office Online - Very simple text substitution: Use string.Replace() - Excel files: Use EPPlus or ClosedXML - Non-.NET environment: Use platform-specific solutions

Q: Can Templify replace reporting tools like Crystal Reports?

A: Partially. Comparison:

Feature Templify Crystal Reports
Data binding ✅ Manual ✅ Automatic
Designer ✅ Word ✅ Proprietary
Conditionals ✅ Yes ✅ Yes
Loops ✅ Yes ✅ Yes
Charts/Graphs ❌ No ✅ Yes
Grouping ⚠️ Manual ✅ Automatic
Export formats Word only Multiple
Cost Free/Open-source Commercial

Use Templify for: Document-centric reports (contracts, letters, proposals) Use Crystal Reports for: Data-heavy reports with charts and complex grouping


Still Have Questions?

Community Support

Documentation

Can't Find Your Answer?

Open a discussion or create an issue on GitHub.


Last Updated: 2025-01-15