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>© {{ 'now'|date('Y') }} Conference Guide</footer> </body></html>