Three kinds of Twig tags
{# comment — does not appear in the HTML #}
{{ variable }}
{{ article.title }}
{{ user.getName() }}
{% if condition %}...{% endif %}
{% for item in collection %}...{% endfor %}
{% block content %}...{% endblock %}
Layout inheritance
This is Twig's most important feature — you avoid duplicating HTML:
{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}FactoryCode{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
{# templates/blog/show.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}{{ article.title }} — Blog{% endblock %}
{% block body %}
<h1>{{ article.title }}</h1>
<div class="content">
{{ article.content|markdown }}
</div>
{% endblock %}
Filters
{{ 'hello world'|upper }} {# HELLO WORLD #}
{{ 'HELLO'|lower }} {# hello #}
{{ article.content|length }} {# number of characters #}
{{ article.content|truncate(200) }} {# shorten to 200 characters #}
{{ price|number_format(2, ',', ' ') }} {# 1 234,56 #}
{{ date|date('d.m.Y H:i') }} {# 23.06.2025 14:30 #}
{{ article.content|nl2br }} {# \n → <br> #}
{{ article.content|markdown }} {# Markdown → HTML #}
{{ rawHtml|raw }} {# disable escaping #}
Conditions and loops
{% if article.isPublished %}
<span class="badge bg-success">Published</span>
{% elseif article.status == 'draft' %}
<span class="badge bg-secondary">Draft</span>
{% else %}
<span class="badge bg-warning">{{ article.status }}</span>
{% endif %}
{% for tag in article.tags %}
<span class="tag">#{{ tag.name }}</span>
{% else %}
<span class="text-muted">No tags</span>
{% endfor %}
Macros — reuse
{% macro alert(message, type = 'info') %}
<div class="alert alert-{{ type }}">
{{ message }}
</div>
{% endmacro %}
{# usage #}
{{ _self.alert('Article saved!', 'success') }}
{{ _self.alert('Validation error', 'danger') }}
include vs embed
{# include — inserts a template, inherits all variables #}
{% include 'components/_card.html.twig' with {article: article} %}
{# embed — inserts a template and lets you override its blocks #}
{% embed 'components/_modal.html.twig' %}
{% block title %}Delete confirmation{% endblock %}
{% block body %}Are you sure you want to delete this article?{% endblock %}
{% endembed %}
A custom Twig extension
// src/Twig/AppExtension.php
class AppExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('reading_time', function (string $content): int {
$words = str_word_count(strip_tags($content));
return (int) ceil($words / 200); // ~200 words/min
}),
];
}
}
{# usage #}
<span>{{ article.content|reading_time }} min read</span>