Back to symfony.com

Lesson 4 of 12 · 4 min

Click any paragraph or press to highlight its code

Twig templates

The render() helper renders a template and wraps the result in a Response object. The second argument is the list of variables the template can use.

Templates are written in Twig, a template language designed to be concise, safe (output is escaped by default) and friendly for designers. {{ ... }} prints a value, and filters like |title transform it.

{% ... %} runs logic, like looping over the speakers array passed by the controller.

The true Twig superpower: inheritance. Write the shared elements (e.g. the header and footer) once in a base.html.twig template, with {% block %} placeholders for the parts that change on each page.

Other templates extend it and fill in only those blocks. That's the entire page: the header and footer come for the parent template.

<?phpnamespace App\Controller;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Routing\Attribute\Route;class ConferenceController extends AbstractController{    #[Route('/conference/{slug}')]    public function show(string $slug): Response    {        return $this->render('conference/show.html.twig', [            'conference' => $slug,            'speakers' => ['Fabien', 'Nicolas', 'Javier'],        ]);    }}
{% extends 'base.html.twig' %}{% block title %}{{ conference|title }} Conference{% endblock %}{% block body %}    <h1>Welcome to {{ conference|title }}!</h1>    <ul>        {% for speaker in speakers %}            <li>{{ speaker }}</li>        {% endfor %}    </ul>{% endblock %}
<!DOCTYPE html><html>    <head>        <meta charset="UTF-8">        <title>{% block title %}Conference Guide{% endblock %}</title>    </head>    <body>        <header>            <nav>{# ... links shared by all pages ... #}</nav>        </header>        {% block body %}{% endblock %}        <footer>&copy; {{ 'now'|date('Y') }} Conference Guide</footer>    </body></html>