Frond (Twig) Templates
TIP
TTina4Twig is a Twig-compatible template engine for server-side rendering, supporting variables, control structures, filters, functions, template inheritance, and macros.
Basic Usage
pascal
var
Twig: TTina4Twig;
Variables: TStringDict;
begin
Twig := TTina4Twig.Create('C:\templates');
Variables := TStringDict.Create;
try
Variables.Add('name', 'Andre');
Variables.Add('items', TValue.From<TArray<String>>(['Apple', 'Banana', 'Cherry']));
Memo1.Lines.Text := Twig.Render('hello.html', Variables);
finally
Variables.Free;
Twig.Free;
end;
end;Variables
Output variables with double curly braces:
{{ name }}
{{ user.email }}Setting Variables
{% set greeting = 'Hello' %}
{% set items = ['Apple', 'Banana'] %}
{% set total = price * quantity %}Control Structures
if / elseif / else
{% if users|length > 0 %}
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
{% elseif guests|length > 0 %}
<p>Guests only</p>
{% else %}
<p>No users found</p>
{% endif %}for loops
{% for item in items %}
<p>{{ item }}</p>
{% endfor %}
{% for key, value in pairs %}
<p>{{ key }}: {{ value }}</p>
{% endfor %}
{% for i in 0..10 %}
<p>{{ i }}</p>
{% endfor %}with
Scopes variables to a block:
{% with { title: 'Hello' } %}
<h1>{{ title }}</h1>
{% endwith %}Template Inheritance
extends and block
base.html:
<html>
<head><title>{% block title %}Default{% endblock %}</title></head>
<body>
{% block content %}{% endblock %}
</body>
</html>page.html:
{% extends 'base.html' %}
{% block title %}My Page{% endblock %}
{% block content %}
<h1>Hello World</h1>
{% endblock %}include
{% include 'header.html' %}
{% include 'sidebar.html' with { menu: items } %}Set the template path so includes resolve correctly:
pascal
Twig := TTina4Twig.Create('C:\MyApp\templates');Macros
Define reusable template fragments:
{% macro input(name, value, type) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{{ input('username', '', 'text') }}
{{ input('password', '', 'password') }}Filters
Filters transform values using the pipe | operator. They can be chained:
{{ name|upper|length }}String Filters
| Filter | Description | Example |
|---|---|---|
upper | Uppercase | <span v-pre>{{ 'hello'|upper }}</span> → HELLO |
lower | Lowercase | <span v-pre>{{ 'HELLO'|lower }}</span> → hello |
capitalize | Capitalize first letter | <span v-pre>{{ 'hello'|capitalize }}</span> → Hello |
title | Title case | <span v-pre>{{ 'hello world'|title }}</span> → Hello World |
trim | Remove whitespace | <span v-pre>{{ ' hi '|trim }}</span> → hi |
nl2br | Newlines to <br> | <span v-pre>{{ text|nl2br }}</span> |
striptags | Remove HTML tags | <span v-pre>{{ html|striptags }}</span> |
replace | Replace values | <span v-pre>{{ 'hello'|replace({'e': 'a'}) }}</span> |
split | Split into array | <span v-pre>{{ 'a,b,c'|split(',') }}</span> |
slug | URL-friendly slug | <span v-pre>{{ 'Hello World'|slug }}</span> → hello-world |
spaceless | Remove whitespace between tags | <span v-pre>{{ html|spaceless }}</span> |
u | Unicode string | <span v-pre>{{ text|u }}</span> |
Number Filters
| Filter | Description | Example |
|---|---|---|
abs | Absolute value | <span v-pre>{{ -5|abs }}</span> → 5 |
number_format | Format number | <span v-pre>{{ 1234.5|number_format(2, '.', ',') }}</span> |
format_number | Format with decimals | <span v-pre>{{ 1234|format_number }}</span> |
format_currency | Format as currency | <span v-pre>{{ 1234|format_currency('USD') }}</span> |
Array Filters
| Filter | Description | Example |
|---|---|---|
length | Length of array/string | <span v-pre>{{ items|length }}</span> |
first | First element | <span v-pre>{{ items|first }}</span> |
last | Last element | <span v-pre>{{ items|last }}</span> |
join | Join into string | <span v-pre>{{ items|join(', ') }}</span> |
keys | Get array keys | <span v-pre>{{ obj|keys }}</span> |
merge | Merge arrays | <span v-pre>{{ arr1|merge(arr2) }}</span> |
sort | Sort array | <span v-pre>{{ items|sort }}</span> |
reverse | Reverse array/string | <span v-pre>{{ items|reverse }}</span> |
shuffle | Randomize order | <span v-pre>{{ items|shuffle }}</span> |
slice | Extract portion | <span v-pre>{{ items|slice(1, 3) }}</span> |
batch | Split into chunks | <span v-pre>{{ items|batch(3) }}</span> |
column | Extract column | <span v-pre>{{ users|column('name') }}</span> |
filter | Filter with callback | <span v-pre>{{ items|filter }}</span> |
find | Find value | <span v-pre>{{ items|find }}</span> |
map | Map with callback | <span v-pre>{{ items|map }}</span> |
reduce | Reduce to single value | <span v-pre>{{ items|reduce }}</span> |
min | Minimum value | <span v-pre>{{ items|min }}</span> |
max | Maximum value | <span v-pre>{{ items|max }}</span> |
Date Filters
| Filter | Description | Example |
|---|---|---|
date | Format date | <span v-pre>{{ post.created|date('Y-m-d') }}</span> |
date_modify | Modify date | <span v-pre>{{ date|date_modify('+1 day') }}</span> |
format_date | Format date (alias) | <span v-pre>{{ date|format_date }}</span> |
format_datetime | Format datetime (alias) | <span v-pre>{{ date|format_datetime }}</span> |
format_time | Format time | <span v-pre>{{ date|format_time }}</span> |
Date format uses PHP-style specifiers that are automatically converted:
| Specifier | Meaning | Example |
|---|---|---|
Y | 4-digit year | 2026 |
y | 2-digit year | 26 |
m | Month (zero-padded) | 03 |
n | Month (no padding) | 3 |
d | Day (zero-padded) | 01 |
j | Day (no padding) | 1 |
H | Hour 24h (zero-padded) | 14 |
h | Hour 12h (zero-padded) | 02 |
i | Minutes | 30 |
s | Seconds | 05 |
A | AM/PM | PM |
D | Short day name | Mon |
l | Full day name | Monday |
M | Short month name | Jan |
F | Full month name | January |
Encoding Filters
| Filter | Description | Example |
|---|---|---|
escape / e | Escape HTML entities | <span v-pre>{{ html|escape }}</span> |
raw | No escaping | <span v-pre>{{ html|raw }}</span> |
url_encode | URL encode | <span v-pre>{{ text|url_encode }}</span> |
json_encode | Encode to JSON | <span v-pre>{{ data|json_encode }}</span> |
json_decode | Decode from JSON | <span v-pre>{{ json|json_decode }}</span> |
convert_encoding | Convert charset | <span v-pre>{{ text|convert_encoding('UTF-8') }}</span> |
data_uri | Create data URI | <span v-pre>{{ content|data_uri }}</span> |
Other Filters
| Filter | Description |
|---|---|
default | Fallback value: <span v-pre>{{ name|default('Guest') }}</span> |
format | String formatting: <span v-pre>{{ 'Hi %s'|format(name) }}</span> |
plural | Plural form |
singular | Singular form |
Functions
| Function | Description | Example |
|---|---|---|
range | Generate number/letter sequence | {% for i in range(1, 10) %} |
dump | Debug output | <span v-pre>{{ dump(variable) }}</span> |
date | Create/format dates | <span v-pre>{{ date('now', 'Y-m-d') }}</span> |
Operators
| Category | Operators |
|---|---|
| Comparison | ==, !=, <, >, <=, >= |
| Logical | and, or, not |
| String | ~ (concatenation), in, starts with, ends with, matches |
| Math | +, -, *, /, %, ** |
| Range | .. (e.g., 1..10, 'a'..'z') |
Integration with TTina4HTMLRender
The HTML renderer has built-in Twig support via its Twig property:
pascal
Tina4HTMLRender1.SetTwigVariable('title', 'Hello');
Tina4HTMLRender1.Twig.Text := '<h1>{{ title }}</h1>';See HTML Renderer - Twig Integration for details.