Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Twig Basics

Twig Basics

~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

Time for our pages to look like REAL web pages. Twig is Symfony's template engine – safe, compact syntax instead of mixing raw PHP into HTML files.

Why a template engine at all?

PHP itself COULD be embedded directly in HTML (<?= $variable ?>) – Twig offers two decisive advantages over raw PHP: AUTOMATIC HTML escaping (protects against XSS by default, chapter 18 goes deeper) and a deliberately RESTRICTED syntax that keeps designers/frontend developers from accidentally writing complex business logic in templates.

Rendering a template from the controller

src/Controller/ProjectController.php
#[Route('/projects', name: 'project_index', methods: ['GET'])]
public function index(): Response
{
    $projects = [
        ['id' => 1, 'name' => 'Website Relaunch'],
        ['id' => 2, 'name' => 'Mobile App'],
    ];

    return $this->render('project/index.html.twig', [
        'projects' => $projects,
    ]);
}

render() (from AbstractController) takes the template path RELATIVE to templates/, plus an array – each key becomes a variable available in the template.

Outputting variables: {{ }}

templates/project/index.html.twig
<h1>Projects</h1>

<ul>
    {% for project in projects %}
        <li>{{ project.name }}</li>
    {% endfor %}
</ul>

{{ ... }} outputs a value (and auto-escapes it), {% ... %} controls LOGIC (loops, conditions) without outputting anything itself. project.name works EQUALLY for array keys ($project['name']) and object properties/getters ($project->getName()) – Twig automatically tries both access forms.

Conditions: {% if %}

{% if projects is empty %}
    <p>No projects yet.</p>
{% else %}
    <ul>
        {% for project in projects %}
            <li>{{ project.name }}</li>
        {% endfor %}
    </ul>
{% endif %}

Important filters: transforming values

{{ project.name|upper }}                {# UPPERCASE #}
{{ project.description|slice(0, 100) }}  {# first 100 characters #}
{{ project.createdAt|date('Y-m-d') }}     {# format a date #}
{{ project.description|default('No description') }}  {# fallback for null/empty #}
{{ project.tags|join(', ') }}             {# array to string #}

Filters get appended with | and can be CHAINED: {{ project.name|lower|trim }} applies lower and then trim in sequence.

Comments in Twig

{# This comment does NOT end up in the rendered HTML #}

The global app variable

Automatically available in EVERY template, without passing it explicitly from the controller:

  • app.user – the logged-in user, or null (block 5).
  • app.request – the current request object.
  • app.flashes – flash messages, already seen in chapter 12.
  • app.environment – the current environment (dev/prod/test).

Checking Twig syntax ahead of time

php bin/console lint:twig templates/

Tipp: lint:twig checks ALL templates for syntax errors, WITHOUT actually calling the application – useful BEFORE every deployment (chapter 48), to catch Twig typos early instead of only on the first page load.