TemplatePro - Powerful Template Engine for Delphi with Jinja-Inspired Syntax
Quick Answer: TemplatePro is Delphi’s most powerful template engine. Use
TTProCompiler.Create.Compile('Hello {{:name}}!'), set data withSetData('name', 'World'), and render with.Render. Supports loops, conditionals, macros, inheritance, and 30+ filters.
Updated 2025-11-25 to TemplatePro Language version 0.8.0.
Here’s an example of a dynamic web application powered by DMVCFramework, TemplatePro and HTMX.

TemplatePro is a modern and versatile templating engine designed to simplify dynamic generation of HTML, email content, and text files. With a syntax inspired by popular templating systems such as Jinja and Smarty, TemplatePro offers powerful features including conditional blocks, loops, template inheritance, reusable macros, and JSON data support.
Documentation for versions prior to 0.8.0 is available here.
This article is also available in Italian.
Since version 0.7.0 TemplatePro is case insensitive in both variable names and directive casing.
Quick Start
The simplest way to use TemplatePro: compile a template string, set variables, and render the output.
var
lCompiler: TTProCompiler;
lCompiledTemplate: ITProCompiledTemplate;
begin
lCompiler := TTProCompiler.Create();
try
lCompiledTemplate := lCompiler.Compile('Hello {{:name}}! You are {{:age}} years old.');
finally
lCompiler.Free;
end;
lCompiledTemplate.SetData('name', 'Daniele');
lCompiledTemplate.SetData('age', 50);
WriteLn(lCompiledTemplate.Render);
end;
Output:
Hello Daniele! You are 50 years old.
That’s it! Compile a template, set some variables, and render. Now let’s explore all the features TemplatePro offers.
Complete Example
For real-world scenarios, you’ll often need filters to transform data and custom functions for specific formatting needs.
program tproconsole;
{$APPTYPE CONSOLE}
uses
TemplatePro,
System.Rtti,
System.SysUtils;
// Custom filter that wraps text in square brackets
function AddSquareBrackets(
const aValue: TValue;
const aParameters: TArray<TFilterParameter>): TValue;
begin
Result := '[' + aValue.AsString + ']';
end;
var
lCompiler: TTProCompiler;
lCompiledTemplate: ITProCompiledTemplate;
begin
lCompiler := TTProCompiler.Create();
try
lCompiledTemplate := lCompiler.Compile(
'Hello {{:name|brackets}}! Today is {{:today|datetostr}}.');
finally
lCompiler.Free;
end;
lCompiledTemplate.SetData('name', 'Daniele Teti');
lCompiledTemplate.SetData('today', Date);
lCompiledTemplate.AddFilter('brackets', AddSquareBrackets);
WriteLn(lCompiledTemplate.Render);
// Output: Hello [Daniele Teti]! Today is 2025-11-25.
end.
For better startup performance in production, save and reload compiled templates from disk:
// Save compiled template to file
lCompiledTemplate.SaveToFile('template.tpc');
// Load pre-compiled template (must be same TemplatePro version)
lCompiledTemplate := TTProCompiledTemplate.CreateFromFile('template.tpc');
1. Variable Interpolation
Use the {{:variable}} syntax to inject dynamic content into templates.
Simple Variables
Display basic values like strings, numbers, or dates directly in the output:
Hello, {{:name}}!
You are {{:age}} years old.
Object Property Access
When working with Delphi objects, access nested properties using dot notation:
{{:user.profile.first_name}} {{:user.profile.last_name}}
Email: {{:user.contact.email}}
Array Element Access
For array or list data, use bracket notation with zero-based indices:
First item: {{:items[0]}}
Second item: {{:items[1]}}
Null or Undefined Variables
Missing or null variables render as empty strings, avoiding runtime errors:
This will be empty if undefined: {{:undefined_var}}
2. Conditional Logic with if/else
Control what content appears based on data conditions.
Basic Conditional Blocks
Show different content to logged-in vs anonymous users:
{{if user_logged_in}}
Welcome back, {{:username}}!
{{else}}
Please log in to continue.
{{endif}}
Truthiness Evaluation
TemplatePro evaluates different data types for truthiness:
- Strings: empty strings are falsy, non-empty strings are truthy
- Numbers: zero is falsy, non-zero is truthy
- Objects: null objects are falsy, non-null objects are truthy
- Collections: empty collections/datasets are falsy, non-empty are truthy
- Boolean: true/false values work as expected
Conditional Expressions with Filters
Use comparison filters to implement business logic like age verification or input validation:
{{if age|ge,18}}
You are an adult.
{{else}}
You are a minor.
{{endif}}
{{if username|contains,"admin"}}
Administrator user detected.
{{endif}}
{{if password|eq,confirm_password}}
Passwords match.
{{else}}
Password confirmation failed.
{{endif}}
Logical Filters and and or
Combine multiple conditions for complex validation scenarios:
{{if value1|and,value2}}
Both are true
{{else}}
At least one is false
{{endif}}
{{if value1|or,value2}}
At least one is true
{{else}}
Both are false
{{endif}}
These filters are useful for checking multiple requirements at once:
{{if user.active|and,user.verified}}
Account fully active
{{endif}}
{{if email_error|or,password_error}}
There are errors in the form
{{endif}}
3. Loops with for .. in
Iterate over collections to generate repeated content like lists, tables, or cards.
Basic Structure
Render a simple list of people from a collection:
{{for person in people}}
- {{:person.first_name}} {{:person.last_name}}
{{endfor}}
Loop Control with continue
Skip certain items based on conditions, like filtering out discontinued products:
{{for product in products}}
{{if product.discontinued|eq,true}}
{{continue}}
{{endif}}
Product: {{:product.name}} - Price: {{:product.price}}
{{endfor}}
Nested Loops
Display hierarchical data like contacts with multiple phone numbers:
{{for person in people}}
{{:person.first_name}} {{:person.last_name}}
{{for phone in person.phones}}
- {{:phone.type}}: {{:phone.number}}
{{endfor}}
{{endfor}}
Pseudo-Variables in Loops
Use built-in loop variables for numbering, zebra striping, or alternating styles:
@@index: current iteration index (1-based)@@odd: true for odd-indexed items@@even: true for even-indexed items
{{for customer in customers}}
{{:customer.@@index}}. {{:customer.Name}}
{{if customer.@@odd}}
(Odd position)
{{endif}}
{{if customer.@@even}}
(Even position)
{{endif}}
{{endfor}}
4. Datasets
Render Delphi TDataSet records directly in templates for database-driven applications:
{{if customers}}
<table>
<tr><th>Code</th><th>Name</th><th>City</th></tr>
{{for customer in customers}}
<tr>
<td>{{:customer.Code}}</td>
<td>{{:customer.Name}}</td>
<td>{{:customer.City}}</td>
</tr>
{{endfor}}
</table>
{{else}}
<p>No customers in database.</p>
{{endif}}
5. Built-in Filters
Transform values on output using the pipe syntax {{:value|filter}}.
Text Transformation Filters
Change text case for consistent display or formatting requirements:
Original: {{:name}}
Uppercase: {{:name|uppercase}}
Lowercase: {{:name|lowercase}}
Capitalized: {{:name|capitalize}}
Padding Filters
Align text in fixed-width outputs like reports or console applications:
Right padded: "{{:code|rpad,10}}"
Right padded with dashes: "{{:code|rpad,10,"-"}}"
Left padded: "{{:code|lpad,8}}"
Left padded with zeros: "{{:id|lpad,6,"0"}}"
Date and Time Filters
Format dates for different locales or display requirements:
System format: {{:birth_date|datetostr}}
Custom format: {{:birth_date|datetostr,"dd/mm/yyyy"}}
DateTime: {{:created_at|datetimetostr,"yyyy-mm-dd hh:nn:ss"}}
ISO format: {{:timestamp|datetoiso8601}}
Numeric Filters
Display numbers with currency symbols, decimal places, or percentage formats:
Formatted: {{:price|formatfloat,"$#,##0.00"}}
Percentage: {{:rate|formatfloat,"0.00%"}}
Text Processing Filters
Truncate long text or escape HTML characters for security:
Truncated: {{:description|trunc,50}}
HTML encoded: {{:user_input|htmlencode}}
Version: {{:""|version}}
The default Filter
Provide fallback values for empty, null, or zero data to avoid blank output:
Value with content: {{:value|default,"fallback"}}
Empty/zero value: {{:zero_value|default,"N/A"}}
Numeric default: {{:quantity|default,1}}
Chain defaults for multi-level fallbacks:
Welcome, {{:nickname|default,name|default,"Guest"}}!
Quantity: {{:qty|default,1}}
Complete Built-in Filters Reference
| Filter | Description | Example |
|---|---|---|
uppercase |
Convert to uppercase | {{:name|uppercase}} |
lowercase |
Convert to lowercase | {{:name|lowercase}} |
capitalize |
Capitalize each word | {{:title|capitalize}} |
rpad,length[,char] |
Right-pad to length | {{:code|rpad,10,"-"}} |
lpad,length[,char] |
Left-pad to length | {{:id|lpad,6,"0"}} |
datetostr[,format] |
Format date | {{:date|datetostr,"dd/mm/yyyy"}} |
datetimetostr[,format] |
Format datetime | {{:datetime|datetimetostr}} |
datetoiso8601 |
ISO 8601 format | {{:date|datetoiso8601}} |
formatfloat,format |
Format numbers | {{:price|formatfloat,"$0.00"}} |
trunc,length |
Truncate text | {{:text|trunc,50}} |
htmlencode |
HTML encode | {{:input|htmlencode}} |
default,value |
Fallback value | {{:var|default,"N/A"}} |
version |
TemplatePro version | {{:|version}} |
totrue |
Always true | {{:var|totrue}} |
tofalse |
Always false | {{:var|tofalse}} |
| Comparison Filters | ||
eq,value |
Equal to | {{if age|eq,25}} |
ne,value |
Not equal to | {{if status|ne,"inactive"}} |
gt,value |
Greater than | {{if score|gt,80}} |
ge,value |
Greater or equal | {{if age|ge,18}} |
lt,value |
Less than | {{if price|lt,100}} |
le,value |
Less or equal | {{if items|le,10}} |
contains,text |
Contains text | {{if name|contains,"John"}} |
icontains,text |
Contains (case-insensitive) | {{if email|icontains,"gmail"}} |
| Logical Filters | ||
and,variable |
Logical AND | {{if a|and,b}} |
or,variable |
Logical OR | {{if a|or,b}} |
6. Chained Filters
Apply multiple transformations in sequence - each filter’s output feeds into the next.
Syntax
{{:variable|filter1|filter2|filter3}}
Practical Examples
Normalize text, combine padding operations, or secure user input with multiple transformations:
{{:name|lowercase|capitalize}}
{{:code|lpad,5|rpad,10}}
{{:description|trunc,50|htmlencode}}
In the first example, the value is first converted to lowercase, then capitalized. In the second, left padding is applied to 5 characters, then right padding to 10 total characters.
Common Use Cases
Build complex formatting pipelines for product codes, user-generated content, or data normalization:
Complex formatting: {{:product_code|uppercase|lpad,10,"0"}}
Output security: {{:user_input|trunc,100|htmlencode}}
Normalization: {{:email|lowercase|default,"noemail@example.com"}}
Chained filters are particularly useful for:
- Applying multiple transformations in a readable way
- Creating data processing pipelines
- Combining formatting and security
7. Custom Filters
Extend TemplatePro with your own transformation functions.
Filter Function Signatures
Define filters as regular functions or anonymous methods:
// Regular function
TTProTemplateFunction =
function(const aValue: TValue; const aParameters: TArray<TFilterParameter>): TValue;
// Anonymous method
TTProTemplateAnonFunction =
reference to function(const aValue: TValue; const aParameters: TArray<TFilterParameter>): TValue;
Simple Custom Filter Example
Create a filter that wraps text in brackets for highlighting:
function AddSquareBrackets(const aValue: TValue;
const aParameters: TArray<TFilterParameter>): TValue;
begin
Result := '[' + aValue.AsString + ']';
end;
// Register the filter
lCompiledTemplate.AddFilter('brackets', AddSquareBrackets);
Usage:
Original: {{:username}}
Bracketed: {{:username|brackets}}
Filter with Parameters
Create a configurable filter that repeats text a specified number of times:
function RepeatText(const aValue: TValue;
const aParameters: TArray<TFilterParameter>): TValue;
var
lText: string;
lCount, i: Integer;
begin
lText := aValue.AsString;
lCount := aParameters[0].ParIntValue;
Result := '';
for i := 1 to lCount do
Result := Result + lText;
end;
lCompiledTemplate.AddFilter('repeat', RepeatText);
Usage:
Repeated: {{:char|repeat,5}}
Variables as Filter Parameters
Pass dynamic values to filters for flexible, data-driven formatting:
{{:message|truncate,max_length}}
{{:price|formatfloat,number_format}}
{{if score|gt,passing_grade}}
Congratulations!
{{endif}}
8. Macros
Define reusable template blocks to avoid repetition and maintain consistency.
Basic Definition and Call
Create a simple greeting macro that can be called with different names:
{{macro greeting(name)}}
Hello, {{:name}}!
{{endmacro}}
{{call greeting("World")}}
{{call greeting("TemplatePro")}}
Output:
Hello, World!
Hello, TemplatePro!
Macros with Multiple Parameters
Build reusable UI components like cards that accept multiple values:
{{macro card(title, content)}}
<div class="card">
<h3>{{:title}}</h3>
<p>{{:content}}</p>
</div>
{{endmacro}}
{{call card("Welcome", "This is the content")}}
{{call card("Second Card", "More content here")}}
Macros without Parameters
Create simple visual separators or decorative elements:
{{macro separator()}}
---
{{endmacro}}
Start
{{call separator()}}
Middle
{{call separator()}}
End
Macros with Filters
Apply transformations inside macros for formatted output:
{{macro format(text)}}
[{{:text|uppercase}}]
{{endmacro}}
{{call format("hello")}}
{{call format(user_name)}}
Macros with Conditional Logic
Create adaptive components that change behavior based on parameters:
{{macro greeting(name, formal)}}
{{if formal}}
Good morning, Mr. {{:name}}.
{{else}}
Hey {{:name}}!
{{endif}}
{{endmacro}}
{{call greeting("Smith", true)}}
{{call greeting("John", false)}}
Nested Macros
Compose complex components by having macros call other macros:
{{macro inner(text)}}
*{{:text}}*
{{endmacro}}
{{macro outer(text)}}
[{{call inner(text)}}]
{{endmacro}}
{{call outer("nested")}}
Output:
[*nested*]
Macros in Loops
Combine macros with loops for consistent item rendering:
{{macro item(name, value)}}
- {{:name}}: {{:value}}
{{endmacro}}
{{for obj in objects}}
{{call item(obj.Name, obj.Value)}}
{{endfor}}
Passing Variables to Macros
Mix literal values and dynamic variables in macro calls:
{{macro showValue(label, value)}}
{{:label}}: {{:value}}
{{endmacro}}
{{call showValue("Name", "John")}}
{{call showValue("Title", page_title)}}
9. Template Functions
Functions are filters applied to empty values - useful for generating dynamic content without input.
Get the current TemplatePro version:
Current version: {{:|version}}
Custom Function Example
Create a UUID generator function for unique identifiers:
function GenerateUUID(const aValue: TValue;
const aParameters: TArray<TFilterParameter>): TValue;
begin
if not aValue.IsEmpty then
raise ETProRenderException.Create('"GenerateUUID" is a function, not a filter');
Result := TGuid.NewGuid.ToString;
end;
lCompiledTemplate.AddFilter('uuid', GenerateUUID);
Usage:
New ID: {{:|uuid}}
10. JSON Data
Render complex nested JSON structures with loops and conditionals:
{{for person in people}}
{{:person.@@index}}) {{:person.first_name|uppercase}} {{:person.last_name|uppercase}}
Age: {{:person.age}}
{{if person.devices}}
Devices:
{{for device in person.devices}}
{{:device.@@index}}. {{:device.name}} ({{:device.type}})
{{endfor}}
{{endif}}
{{endfor}}
11. Template Inheritance
Build consistent page layouts by extending a base template with custom blocks.
Base Template (base.tpro)
Define the common structure with replaceable blocks:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{block "title"}}Default Title{{endblock}}</title>
{{block "extra_css"}}{{endblock}}
</head>
<body>
<header>
{{block "navigation"}}
<nav>Default menu</nav>
{{endblock}}
</header>
<main>
{{block "content"}}
<p>Default content.</p>
{{endblock}}
</main>
<footer>
{{block "footer"}}
<p>Default footer</p>
{{endblock}}
</footer>
{{block "extra_js"}}{{endblock}}
</body>
</html>
Child Template
Override specific blocks while inheriting the rest:
{{extends "base.tpro"}}
{{block "title"}}{{:page_title}} - My Website{{endblock}}
{{block "content"}}
<h1>{{:page_title}}</h1>
<p>{{:content}}</p>
{{endblock}}
{{block "extra_js"}}
<script src="/js/page.js"></script>
{{endblock}}
Inheritance Rules
- Single-level inheritance: child templates can extend only one parent template
- Block overriding: if a child defines an existing block, it overrides the parent’s block
- Default blocks: if a child doesn’t define a block, the parent’s default content is used
- Ignored content: any content outside blocks in child templates is ignored
12. Template Inclusion
Split templates into reusable files for headers, footers, and shared components:
{{include "header.tpro"}}
<main>
<h1>{{:page_title}}</h1>
<p>{{:content}}</p>
</main>
{{include "footer.tpro"}}
Inclusion with Subdirectories
Organize templates in folders for better maintainability:
{{include "partials/navigation.tpro"}}
{{include "components/product-card.tpro"}}
13. Nullable Types
Handle optional values gracefully with conditional checks:
{{if user.middle_name}}
Full name: {{:user.first_name}} {{:user.middle_name}} {{:user.last_name}}
{{else}}
Name: {{:user.first_name}} {{:user.last_name}}
{{endif}}
{{if order.shipped_date}}
Shipped: {{:order.shipped_date|datetostr}}
{{else}}
Status: Processing
{{endif}}
14. Best Practices and Performance
Template Compilation
- Compile templates once and reuse the compiled version
- Save compiled templates to files for better startup performance
- Use appropriate caching strategies for production environments
Filter Usage
- Use built-in filters when possible (they’re optimized)
- Minimize complex custom filters in loops
- Consider pre-processing data instead of complex template logic
Template Organization
- Use inheritance for consistent layouts
- Split large templates into smaller, reusable components
- Organize templates in logical directory structures
- Use macros for repeated components
Error Handling
Wrap template operations in try-except blocks to handle compilation and rendering errors:
try
lCompiledTemplate := lCompiler.Compile(lTemplate);
lResult := lCompiledTemplate.Render;
except
on E: ETProCompilerException do
// Handle compilation errors
ShowMessage('Template compilation error: ' + E.Message);
on E: ETProRenderException do
// Handle rendering errors
ShowMessage('Template rendering error: ' + E.Message);
on E: Exception do
// Handle other errors
ShowMessage('Unexpected error: ' + E.Message);
end;
Related Open Source Projects
TemplatePro integrates with other open source projects by the same author:
| Project | Description | Integration |
|---|---|---|
| DMVCFramework | REST API framework for Delphi | Server-side HTML rendering with serversideviews_templatepro and HTMX samples |
| Delphi Fake Data Utils | Test data generator | Generate realistic sample data for template previews and demos |
| DelphiGuard | RAII memory management | Automatic cleanup for template compilers and resources |
Conclusion
TemplatePro is a complete and powerful templating engine that provides developers with the flexibility and control needed for modern application development. Its key features include:
- Intuitive Syntax: clean, readable syntax inspired by popular engines
- Powerful Logic: comprehensive conditional statements and loop constructs
- Template Inheritance: reusable layouts and component hierarchies
- Macros: reusable code blocks with parameters
- Chained Filters: flexible data transformation pipelines
- Extensive Filters: rich set of built-in filters plus custom filter support
- JSON Excellence: first-class support for complex JSON data structures
- Type Safety: excellent handling of Delphi’s type system including nullables
- Modular Design: template inclusion and component-based development
Whether you’re building simple HTML emails, complex web applications, or dynamic report generation systems, TemplatePro provides the tools needed to create maintainable, efficient, and powerful templates.
Links and Resources
- TemplatePro GitHub Repository
- Unit tests for comprehensive examples and edge cases
- DelphiMVCFramework Support Group on Facebook (includes TemplatePro support)
- Support the Project on Patreon
- Previous documentation
FAQ: Frequently Asked Questions
What is the best template engine for Delphi?
TemplatePro is a powerful, feature-rich template engine for Delphi with Jinja/Smarty-inspired syntax. It supports conditionals, loops, template inheritance, macros, filters, and JSON data - everything you need for generating HTML, emails, and reports.
How do I generate HTML in Delphi?
Use TemplatePro:
uses TemplatePro;
var lCompiler := TTProCompiler.Create;
var lTemplate := lCompiler.Compile('<h1>Hello {{:name}}!</h1>');
lTemplate.SetData('name', 'World');
var HTML := lTemplate.Render; // "<h1>Hello World!</h1>"
Does TemplatePro support template inheritance?
Yes, TemplatePro supports template inheritance with {{extends "base.tpro"}} and {{block "name"}}...{{endblock}} syntax, similar to Jinja2. Child templates can override specific blocks while inheriting the rest.
How do I create email templates in Delphi?
Use TemplatePro to compile your email template with placeholders, then set the data and render:
var lTemplate := lCompiler.Compile(LoadFromFile('email_template.html'));
lTemplate.SetData('customer_name', 'John');
lTemplate.SetData('order_total', 99.99);
var EmailBody := lTemplate.Render;
Does TemplatePro support loops?
Yes, use {{for item in items}}...{{endfor}} syntax:
{{for product in products}}
<li>{{:product.name}} - ${{:product.price}}</li>
{{endfor}}
How do I use conditionals in TemplatePro?
Use {{if condition}}...{{else}}...{{endif}}:
{{if user.is_admin}}
<a href="/admin">Admin Panel</a>
{{else}}
<a href="/profile">My Profile</a>
{{endif}}
What are TemplatePro filters?
Filters transform values using pipe syntax. Built-in filters include uppercase, lowercase, datetostr, htmlencode, trunc, and more:
{{:name|uppercase}}
{{:price|formatfloat,"$0.00"}}
{{:date|datetostr,"dd/mm/yyyy"}}
Can I create custom filters in TemplatePro?
Yes, define a function and register it:
function MyFilter(const aValue: TValue; const aParams: TArray<TFilterParameter>): TValue;
begin
Result := '[' + aValue.AsString + ']';
end;
lTemplate.AddFilter('brackets', MyFilter);
Does TemplatePro support macros?
Yes, define reusable template blocks with parameters:
{{macro button(text, url)}}
<a href="{{:url}}" class="btn">{{:text}}</a>
{{endmacro}}
{{call button("Click Me", "/action")}}
How do I use TemplatePro with JSON data?
TemplatePro has first-class JSON support. Load JSON and access nested properties:
{{for person in people}}
{{:person.name}} - {{:person.address.city}}
{{endfor}}
Can I compile templates once and reuse them?
Yes, for better performance compile once and reuse:
// Compile once
var lTemplate := lCompiler.Compile(TemplateString);
// Save to file
lTemplate.SaveToFile('template.tpc');
// Load pre-compiled
var lTemplate := TTProCompiledTemplate.CreateFromFile('template.tpc');
Is TemplatePro case-sensitive?
Since version 0.7.0, TemplatePro is case insensitive for both variable names and directive keywords.
Does TemplatePro work with DMVCFramework?
Yes, TemplatePro integrates seamlessly with DMVCFramework for server-side HTML rendering. See the serversideviews_templatepro and htmx_website_with_templatepro samples.
Is TemplatePro free?
Yes, TemplatePro is free and open-source under the Apache 2.0 license.
TemplatePro - Powerful template engine for Delphi. Jinja-inspired. Feature-rich. Open-source.
Current Version: 0.8.0 - Updated November 2025
Comments
comments powered by Disqus