Frequently Asked Questions (FAQ)¶
Common questions and answers about using Templify for Word document templating.
Table of Contents¶
- Getting Started
- Features & Capabilities
- Syntax & Usage
- Performance
- Troubleshooting
- Migration
- Enterprise & Production
- Comparison
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:
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:
Q: How do conditionals work?¶
A: Use {{#if}}...{{#else}}...{{/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:
Data:
["Items"] = new List<object>
{
new { Name = "Product A", Price = 10.00m },
new { Name = "Product B", Price = 20.00m }
}
Output:
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:
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:
C# Data:
var data = new Dictionary<string, object>
{
["IsCompleted"] = true,
["IsApproved"] = false,
["IsValid"] = true
};
JSON Data:
Output:
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:
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:
Data:
Output:
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:
Output:
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): Sí 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:
Q: How do I debug template issues?¶
A: Steps:
1. Check ProcessingResult:
-
Review missing variables:
-
Simplify template: Remove complexity until it works, then add back
-
Check template structure: Use Converter's analyze command:
Q: Can I see what Templify found in my template?¶
A: Yes! Use the Converter tool:
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:
Step 2: Convert:
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¶
- 💬 GitHub Discussions - Ask the community
- 🐛 GitHub Issues - Report bugs
- 📖 Full Documentation - Complete reference
Documentation¶
Can't Find Your Answer?¶
Open a discussion or create an issue on GitHub.
Last Updated: 2025-01-15