symfony/twig-bundle
Symfony TwigBundle integrates the Twig templating engine into the Symfony full-stack framework, providing seamless configuration, services, and rendering support for templates and views within Symfony applications.
Installation Add the bundle via Composer (included by default in Symfony Flex):
composer require symfony/twig-bundle
No additional configuration is needed for basic usage—it auto-registers with Symfony’s kernel.
First Use Case: Rendering a Template
Create a template at templates/base.html.twig:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Welcome!{% endblock %}</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
Render it in a controller:
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class HomeController extends AbstractController
{
public function index(): Response
{
return $this->render('base.html.twig', [
'title' => 'Custom Title',
]);
}
}
Where to Look First
config/packages/twig.yaml (auto-generated).{{ dump() }} in templates or var_dump() in PHP for debugging.Template Inheritance
Extend base templates with {% extends 'base.html.twig' %} and override blocks ({% block body %}).
Example: Create templates/home.html.twig:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Hello, {{ name }}!</h1>
{% endblock %}
Passing Data to Templates
Use the render() method in controllers or pass data via ->with():
$this->render('home.html.twig', ['name' => 'John']);
Embedding Components
Reuse template fragments with {% include %} or {% embed %}:
{% include 'partials/_navbar.html.twig' %}
{% embed 'layouts/app.html.twig' %}
{% block content %}{% endblock %}
{% endembed %}
Global Variables
Define globals in twig.yaml:
twig:
globals:
app_name: 'MyApp'
Access them in templates: {{ app_name }}.
Asset Management
Use {{ asset() }} for static files (configured in framework.yaml):
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
{{ form(row) }} (requires Symfony\Bridge\Twig\Extension\FormExtension).{{ 'home.title'|trans }} (configured in framework.yaml).config/packages/dev/twig.yaml:
twig:
debug: true
// src/Twig/AppExtension.php
class AppExtension extends \Twig\Extension\AbstractExtension
{
public function getFunctions(): array
{
return [
new \Twig\TwigFunction('greet', [$this, 'greetUser']),
];
}
public function greetUser(string $name): string
{
return "Hello, $name!";
}
}
Register it as a service (auto-discovered via autoconfigure: true).Caching Headaches
php bin/console cache:clear
twig.debug: true) disables caching but slows performance.Autoloading Issues
bin/console debug:autowiring to verify services.templates/, templates/{controller_name}/.Variable Scope
render() are only available in that template unless extended.{% set %} for local variables or globals for app-wide access.Deprecated Syntax
{{ include }} (use {% include %}).{{ form_widget() }} over {{ form() }} for clarity.Security
{{ user.input }} (safe) vs. {{ user.input|raw }} (unsafe).{{ form_row(form.field) }} to auto-escape form fields.var/log/dev.log or enable Twig’s debug mode.{{ dump(var) }} or {{ var|default('fallback') }}._profiler route) or twig:debug command:
php bin/console twig:debug
Custom Paths
Add custom template paths in twig.yaml:
twig:
paths:
'%kernel.project_dir%/templates/custom': ~
Environment-Specific Config
Override settings per environment (e.g., config/packages/dev/twig.yaml):
twig:
strict_variables: true # Fail on undefined vars
Event Listeners
Listen to Twig events (e.g., Twig\Event\SourceContextLoadedEvent) for runtime modifications:
// src/EventListener/TwigListener.php
class TwigListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
TwigEvent::SOURCE_CONTEXT_LOADED => 'onSourceContextLoaded',
];
}
public function onSourceContextLoaded(TwigEvent $event): void
{
$context = $event->getSourceContext();
// Modify template logic here
}
}
Runtime Template Overrides
Use Twig\Environment::createTemplate() to dynamically generate templates:
$template = $this->get('twig')->createTemplate('Hello {{ name }}!');
echo $template->render(['name' => 'Alice']);
{% cache %} for static fragments:
{% cache 'sidebar' %}
{% include 'partials/sidebar.html.twig' %}
{% endcache %}
_macros.html.twig:
{% macro alert(type, message) %}
<div class="alert alert-{{ type }}">{{ message }}</div>
{% endmacro %}
Include and use:
{% import '_macros.html.twig' as macros %}
{{ macros.alert('success', 'Saved!') }}
{{ stimulus_controller('hello_controller', {'name': 'World'}) }}
How can I help you explore Laravel packages today?