Become a member!

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 with SetData('name', 'World'), and render with .Render. Supports loops, conditionals, macros, inheritance, and 30+ filters.

TemplatePro Logo

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

  1. Single-level inheritance: child templates can extend only one parent template
  2. Block overriding: if a child defines an existing block, it overrides the parent’s block
  3. Default blocks: if a child doesn’t define a block, the parent’s default content is used
  4. 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;

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.


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