Creating and Using Templates

A template is the best way to organize and render HTML from inside your application,whether you need to render HTML from a controller or generatethe contents of an email. Templates in Symfony are created withTwig: a flexible, fast, and secure template engine.

Twig Templating Language

The Twig templating language allows you to write concise, readable templatesthat are more friendly to web designers and, in several ways, more powerful thanPHP templates. Take a look at the following Twig template example. Even if it'sthe first time you see Twig, you probably understand most of it:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Welcome to Symfony!</title>
  5. </head>
  6. <body>
  7. <h1>{{ page_title }}</h1>
  8.  
  9. {% if user.isLoggedIn %}
  10. Hello {{ user.name }}!
  11. {% endif %}
  12.  
  13. {# ... #}
  14. </body>
  15. </html>

Twig syntax is based on these three constructs:

  • {{ … }}, used to display the content of a variable or the result ofevaluating an expression;
  • {% … %}, used to run some logic, such as a conditional or a loop;
  • {# … #}, used to add comments to the template (unlike HTML comments,these comments are not included in the rendered page).You can't run PHP code inside Twig templates, but Twig provides utilities torun some logic in the templates. For example, filters modify content beforebeing rendered, like the upper filter to uppercase contents:
  1. {{ title|upper }}

Twig comes with a long list of tags, filters and functions that areavailable by default. In Symfony applications you can also use theseTwig filters and functions defined by Symfonyand you can create your own Twig filters and functions.

Twig is fast in the prod environment(because templates are compiled into PHP and cached automatically), butconvenient to use in the dev environment (because templates are recompiledautomatically when you change them).

Twig Configuration

Twig has several configuration options to define things like the format usedto display numbers and dates, the template caching, etc. Read theTwig configuration reference to learn about them.

Creating Templates

Before explaining in detail how to create and render templates, look at thefollowing example for a quick overview of the whole process. First, you need tocreate a new file in the templates/ directory to store the template contents:

  1. {# templates/user/notifications.html.twig #}
  2. <h1>Hello {{ user_first_name }}!</h1>
  3. <p>You have {{ notifications|length }} new notifications.</p>

Then, create a controller that renders this template andpasses to it the needed variables:

  1. // src/Controller/UserController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5.  
  6. class UserController extends AbstractController
  7. {
  8. // ...
  9.  
  10. public function notifications()
  11. {
  12. // get the user information and notifications somehow
  13. $userFirstName = '...';
  14. $userNotifications = ['...', '...'];
  15.  
  16. // the template path is the relative file path from `templates/`
  17. return $this->render('user/notifications.html.twig', [
  18. // this array defines the variables passed to the template,
  19. // where the key is the variable name and the value is the variable value
  20. // (Twig recommends using snake_case variable names: 'foo_bar' instead of 'fooBar')
  21. 'user_first_name' => $userFirstName,
  22. 'notifications' => $userNotifications,
  23. ]);
  24. }
  25. }

Template Naming

Symfony recommends the following for template names:

  • Use snake case for filenames and directories (e.g. blog_posts.twig,admin/default_theme/blog/index.twig, etc.);
  • Define two extensions for filenames (e.g. index.html.twig orblog_posts.xml.twig) being the first extension (html, xml, etc.)the final format that the template will generate.Although templates usually generate HTML contents, they can generate anytext-based format. That's why the two-extension convention simplifies the waytemplates are created and rendered for multiple formats.

Template Location

Templates are stored by default in the templates/ directory. When a serviceor controller renders the product/index.html.twig template, they are actuallyreferring to the <your-project>/templates/product/index.html.twig file.

The default templates directory is configurable with thetwig.default_path option and you can add moretemplate directories as explained later in this article.

Template Variables

A common need for templates is to print the values stored in the templatespassed from the controller or service. Variables usually store objects andarrays instead of strings, numbers and boolean values. That's why Twig providesquick access to complex PHP variables. Consider the following template:

  1. <p>{{ user.name }} added this comment on {{ comment.publishedAt|date }}</p>

The user.name notation means that you want to display some information(name) stored in a variable (user). Is user an array or an object?Is name a property or a method? In Twig this doesn't matter.

When using the foo.bar notation, Twig tries to get the value of the variablein the following order:

  • $foo['bar'] (array and element);
  • $foo->bar (object and public property);
  • $foo->bar() (object and public method);
  • $foo->getBar() (object and getter method);
  • $foo->isBar() (object and isser method);
  • $foo->hasBar() (object and hasser method);
  • If none of the above exists, use null.This allows to evolve your application code without having to change thetemplate code (you can start with array variables for the application proof ofconcept, then move to objects with methods, etc.)

Linking to Pages

Instead of writing the link URLs by hand, use the path() function togenerate URLs based on the routing configuration.

Later, if you want to modify the URL of a particular page, all you'll need to dois change the routing configuration: the templates will automatically generatethe new URL.

Consider the following routing configuration:

  • Annotations
  1. // src/Controller/BlogController.php
  2.  
  3. // ...
  4. use Symfony\Component\Routing\Annotation\Route;
  5.  
  6. class BlogController extends AbstractController
  7. {
  8. /**
  9. * @Route("/", name="blog_index")
  10. */
  11. public function index()
  12. {
  13. // ...
  14. }
  15.  
  16. /**
  17. * @Route("/article/{slug}", name="blog_post")
  18. */
  19. public function show(string $slug)
  20. {
  21. // ...
  22. }
  23. }
  • YAML
  1. # config/routes.yaml
  2. blog_index:
  3. path: /
  4. controller: App\Controller\BlogController::index
  5.  
  6. blog_post:
  7. path: /article/{slug}
  8. controller: App\Controller\BlogController::show
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_index"
  9. path="/"
  10. controller="App\Controller\BlogController::index"/>
  11.  
  12. <route id="blog_post"
  13. path="/article/{slug}"
  14. controller="App\Controller\BlogController::show"/>
  15. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_index', '/')
  7. ->controller([BlogController::class, 'index'])
  8. ;
  9.  
  10. $routes->add('blog_post', '/articles/{slug}')
  11. ->controller([BlogController::class, 'show'])
  12. ;
  13. };

Use the path() Twig function to link to these pages and pass the route nameas the first argument and the route parameters as the optional second argument:

  1. <a href="{{ path('blog_index') }}">Homepage</a>
  2.  
  3. {# ... #}
  4.  
  5. {% for post in blog_posts %}
  6. <h1>
  7. <a href="{{ path('blog_post', {slug: post.slug}) }}">{{ post.title }}</a>
  8. </h1>
  9.  
  10. <p>{{ post.excerpt }}</p>
  11. {% endfor %}

The path() function generates relative URLs. If you need to generateabsolute URLs (for example when rendering templates for emails or RSS feeds),use the url() function, which takes the same arguments as path()(e.g. <a href="{{ url('blog_index') }}"> … </a>).

Linking to CSS, JavaScript and Image Assets

If a template needs to link to a static asset (e.g. an image), Symfony providesan asset() Twig function to help generate that URL. First, install theasset package:

  1. $ composer require symfony/asset

You can now use the asset() function:

  1. {# the image lives at "public/images/logo.png" #}
  2. <img src="{{ asset('images/logo.png') }}" alt="Symfony!"/>
  3.  
  4. {# the CSS file lives at "public/css/blog.css" #}
  5. <link href="{{ asset('css/blog.css') }}" rel="stylesheet"/>
  6.  
  7. {# the JS file lives at "public/bundles/acme/js/loader.js" #}
  8. <script src="{{ asset('bundles/acme/js/loader.js') }}"></script>

The asset() function's main purpose is to make your application more portable.If your application lives at the root of your host (e.g. https://example.com),then the rendered path should be /images/logo.png. But if your applicationlives in a subdirectory (e.g. https://example.com/my_app), each asset pathshould render with the subdirectory (e.g. /my_app/images/logo.png). Theasset() function takes care of this by determining how your application isbeing used and generating the correct paths accordingly.

Tip

The asset() function supports various cache busting techniques via theversion,version_format, andjson_manifest_path configuration options.

Tip

If you'd like help packaging, versioning and minifying your JavaScript andCSS assets in a modern way, read about Symfony's Webpack Encore.

If you need absolute URLs for assets, use the absolute_url() Twig functionas follows:

  1. <img src="{{ absolute_url(asset('images/logo.png')) }}" alt="Symfony!"/>
  2.  
  3. <link rel="shortcut icon" href="{{ absolute_url('favicon.png') }}">

The App Global Variable

Symfony creates a context object that is injected into every Twig templateautomatically as a variable called app. It provides access to someapplication information:

  1. <p>Username: {{ app.user.username ?? 'Anonymous user' }}</p>
  2. {% if app.debug %}
  3. <p>Request method: {{ app.request.method }}</p>
  4. <p>Application Environment: {{ app.environment }}</p>
  5. {% endif %}

The app variable (which is an instance of AppVariable)gives you access to these variables:

Rendering Templates

Rendering a Template in Controllers

If your controller extends from the AbstractController,use the render() helper:

  1. // src/Controller/ProductController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5.  
  6. class ProductController extends AbstractController
  7. {
  8. public function index()
  9. {
  10. // ...
  11.  
  12. return $this->render('product/index.html.twig', [
  13. 'category' => '...',
  14. 'promotions' => ['...', '...'],
  15. ]);
  16. }
  17. }

If your controller does not extend from AbstractController, you'll need tofetch services in your controller anduse the render() method of the twig service.

Rendering a Template in Services

Inject the twig Symfony service into your own services and use itsrender() method. When using service autowiringyou only need to add an argument in the service constructor and type-hint it withthe Environment class:

  1. // src/Service/SomeService.php
  2. use Twig\Environment;
  3.  
  4. class SomeService
  5. {
  6. private $twig;
  7.  
  8. public function __construct(Environment $twig)
  9. {
  10. $this->twig = $twig;
  11. }
  12.  
  13. public function someMethod()
  14. {
  15. // ...
  16.  
  17. $htmlContents = $this->twig->render('product/index.html.twig', [
  18. 'category' => '...',
  19. 'promotions' => ['...', '...'],
  20. ]);
  21. }
  22. }

Rendering a Template in Emails

Read the docs about the mailer and Twig integration.

Rendering a Template Directly from a Route

Although templates are usually rendered in controllers and services, you canrender static pages that don't need any variables directly from the routedefinition. Use the special TemplateController provided by Symfony:

  • YAML
  1. # config/routes.yaml
  2. acme_privacy:
  3. path: /privacy
  4. controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
  5. defaults:
  6. # the path of the template to render
  7. template: 'static/privacy.html.twig'
  8.  
  9. # special options defined by Symfony to set the page cache
  10. maxAge: 86400
  11. sharedAge: 86400
  12.  
  13. # optionally you can define some arguments passed to the template
  14. site_name: 'ACME'
  15. theme: 'dark'
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd">
  6.  
  7. <route id="acme_privacy"
  8. path="/privacy"
  9. controller="Symfony\Bundle\FrameworkBundle\Controller\TemplateController">
  10. <!-- the path of the template to render -->
  11. <default key="template">static/privacy.html.twig</default>
  12.  
  13. <!-- special options defined by Symfony to set the page cache -->
  14. <default key="maxAge">86400</default>
  15. <default key="sharedAge">86400</default>
  16.  
  17. <!-- optionally you can define some arguments passed to the template -->
  18. <default key="site_name">ACME</default>
  19. <default key="theme">dark</default>
  20. </route>
  21. </routes>
  • PHP
  1. // config/routes.php
  2. use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('acme_privacy', '/privacy')
  7. ->controller(TemplateController::class)
  8. ->defaults([
  9. // the path of the template to render
  10. 'template' => 'static/privacy.html.twig',
  11.  
  12. // special options defined by Symfony to set the page cache
  13. 'maxAge' => 86400,
  14. 'sharedAge' => 86400,
  15.  
  16. // optionally you can define some arguments passed to the template
  17. 'site_name' => 'ACME',
  18. 'theme' => 'dark',
  19. ])
  20. ;
  21. };

Checking if a Template Exists

Templates are loaded in the application using a Twig template loader, whichalso provides a method to check for template existence. First, get the loader:

  1. // in a controller extending from AbstractController
  2. $loader = $this->get('twig')->getLoader();
  3.  
  4. // in a service using autowiring
  5. use Twig\Environment;
  6.  
  7. public function __construct(Environment $twig)
  8. {
  9. $loader = $twig->getLoader();
  10. }

Then, pass the path of the Twig template to the exists() method of the loader:

  1. if ($loader->exists('theme/layout_responsive.html.twig')) {
  2. // the template exists, do something
  3. // ...
  4. }

Debugging Templates

Symfony provides several utilities to help you debug issues in your templates.

Linting Twig Templates

The lint:twig command checks that your Twig templates don't have any syntaxerrors. It's useful to run it before deploying your application to production(e.g. in your continuous integration server):

  1. # check all the templates stored in a directory
  2. $ php bin/console lint:twig templates/
  3.  
  4. # you can also check individual templates
  5. $ php bin/console lint:twig templates/article/recent_list.html.twig

Inspecting Twig Information

The debug:twig command lists all the information available about Twig(functions, filters, global variables, etc.). It's useful to check if yourcustom Twig extensions are working properlyand also to check the Twig features added when installing packages:

  1. # list general information
  2. $ php bin/console debug:twig
  3.  
  4. # filter output by any keyword
  5. $ php bin/console debug:twig --filter=date
  6.  
  7. # pass a template path to show the physical file which will be loaded
  8. $ php bin/console debug:twig @Twig/Exception/error.html.twig

The Dump Twig Utilities

Symfony provides a dump() function as animproved alternative to PHP's var_dump() function. This function is usefulto inspect the contents of any variable and you can use it in Twig templates too.

First, make sure that the VarDumper component is installed in the application:

  1. $ composer require symfony/var-dumper

Then, use either the {% dump %} tag or the {{ dump() }} functiondepending on your needs:

  1. {# templates/article/recent_list.html.twig #}
  2. {# the contents of this variable are sent to the Web Debug Toolbar
  3. instead of dumping them inside the page contents #}
  4. {% dump articles %}
  5.  
  6. {% for article in articles %}
  7. {# the contents of this variable are dumped inside the page contents
  8. and they are visible on the web page #}
  9. {{ dump(article) }}
  10.  
  11. <a href="/article/{{ article.slug }}">
  12. {{ article.title }}
  13. </a>
  14. {% endfor %}

To avoid leaking sensitive information, the dump() function/tag is onlyavailable in the dev and test configuration environments.If you try to use it in the prod environment, you will see a PHP error.

Reusing Template Contents

Including Templates

If certain Twig code is repeated in several templates, you can extract it into asingle "template fragment" and include it in other templates. Imagine that thefollowing code to display the user information is repeated in several places:

  1. {# templates/blog/index.html.twig #}
  2.  
  3. {# ... #}
  4. <div class="user-profile">
  5. <img src="{{ user.profileImageUrl }}"/>
  6. <p>{{ user.fullName }} - {{ user.email }}</p>
  7. </div>

First, create a new Twig template called blog/user_profile.html.twig (the prefix is optional, but it's a convention used to better differentiatebetween full templates and template fragments).

Then, remove that content from the original blog/index.html.twig templateand add the following to include the template fragment:

  1. {# templates/blog/index.html.twig #}
  2.  
  3. {# ... #}
  4. {{ include('blog/_user_profile.html.twig') }}

The include() Twig function takes as argument the path of the template toinclude. The included template has access to all the variables of the templatethat includes it (use the with_context option to control this).

You can also pass variables to the included template. This is useful for exampleto rename variables. Imagine that your template stores the user information in avariable called blogpost.author instead of the user variable that thetemplate fragment expects. Use the following to _rename the variable:

  1. {# templates/blog/index.html.twig #}
  2.  
  3. {# ... #}
  4. {{ include('blog/_user_profile.html.twig', {user: blog_post.author}) }}

Embedding Controllers

Including template fragments is useful to reuse thesame content on several pages. However, this technique is not the best solutionin some cases.

Imagine that the template fragment displays the three most recent blog articles.To do that, it needs to make a database query to get those articles. When usingthe include() function, you'd need to do the same database query in everypage that includes the fragment. This is not very convenient.

A better alternative is to embed the result of executing some controllerwith the render() and controller() Twig functions.

First, create the controller that renders a certain number of recent articles:

  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. // ...
  5.  
  6. class BlogController extends AbstractController
  7. {
  8. public function recentArticles($max = 3)
  9. {
  10. // get the recent articles somehow (e.g. making a database query)
  11. $articles = ['...', '...', '...'];
  12.  
  13. return $this->render('blog/_recent_articles.html.twig', [
  14. 'articles' => $articles
  15. ]);
  16. }
  17. }

Then, create the blog/recent_articles.html.twig template fragment (the prefix in the template name is optional, but it's a convention used tobetter differentiate between full templates and template fragments):

  1. {# templates/blog/_recent_articles.html.twig #}
  2. {% for article in articles %}
  3. <a href="{{ path('blog_show', {slug: article.slug}) }}">
  4. {{ article.title }}
  5. </a>
  6. {% endfor %}

Now you can call to this controller from any template to embed its result:

  1. {# templates/base.html.twig #}
  2.  
  3. {# ... #}
  4. <div id="sidebar">
  5. {# if the controller is associated with a route, use the path() or url() functions #}
  6. {{ render(path('latest_articles', {max: 3})) }}
  7. {{ render(url('latest_articles', {max: 3})) }}
  8.  
  9. {# if you don't want to expose the controller with a public URL,
  10. use the controller() function to define the controller to execute #}
  11. {{ render(controller(
  12. 'App\\Controller\\BlogController::recentArticles', {max: 3}
  13. )) }}
  14. </div>

When using the controller() function, controllers are not accessed using aregular Symfony route but through a special URL used exclusively to serve thosetemplate fragments. Configure that special URL in the fragments option:

  • YAML
  1. # config/packages/framework.yaml
  2. framework:
  3. # ...
  4. fragments: { path: /_fragment }
  • XML
  1. <!-- config/packages/framework.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <container xmlns="http://symfony.com/schema/dic/services"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:framework="http://symfony.com/schema/dic/symfony"
  6. xsi:schemaLocation="http://symfony.com/schema/dic/services
  7. https://symfony.com/schema/dic/services/services-1.0.xsd
  8. http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
  9.  
  10. <!-- ... -->
  11. <framework:config>
  12. <framework:fragment path="/_fragment"/>
  13. </framework:config>
  14. </container>
  • PHP
  1. // config/packages/framework.php
  2. $container->loadFromExtension('framework', [
  3. // ...
  4. 'fragments' => ['path' => '/_fragment'],
  5. ]);

Caution

Embedding controllers require making requests to those controllers andrendering some templates as result. This can have a significant impact inthe application performance if you embed lots of controllers. If possible,cache the template fragment.

Templates can also embed contents asynchronouslywith the hinclude.js JavaScript library.

Template Inheritance and Layouts

As your application grows you'll find more and more repeated elements betweenpages, such as headers, footers, sidebars, etc. Including templatesand embedding controllers can help, butwhen pages share a common structure, it's better to use inheritance.

The concept of Twig template inheritance is similar to PHP class inheritance.You define a parent template that other templates can extend from and childtemplates can override parts of the parent template.

Symfony recommends the following three-level template inheritance for medium andcomplex applications:

  • templates/base.html.twig, defines the common elements of all applicationtemplates, such as <head>, <header>, <footer>, etc.;
  • templates/layout.html.twig, extends from base.html.twig and definesthe content structure used in all or most of the pages, such as a two-columncontent + sidebar layout. Some sections of the application can define theirown layouts (e.g. templates/blog/layout.html.twig);
  • templates/*.html.twig, the application pages which extend from the mainlayout.html.twig template or any other section layout.In practice, the base.html.twig template would look like this:
  1. {# templates/base.html.twig #}
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>{% block title %}My Application{% endblock %}</title>
  7. </head>
  8. <body>
  9. <div id="sidebar">
  10. {% block sidebar %}
  11. <ul>
  12. <li><a href="{{ path('homepage') }}">Home</a></li>
  13. <li><a href="{{ path('blog_index') }}">Blog</a></li>
  14. </ul>
  15. {% endblock %}
  16. </div>
  17.  
  18. <div id="content">
  19. {% block body %}{% endblock %}
  20. </div>
  21. </body>
  22. </html>

The Twig block tag defines the page sections that can be overridden in thechild templates. They can be empty, like the body block or define a defaultcontent, like the title block, which is displayed when child templates don'toverride them.

The blog/layout.html.twig template could be like this:

  1. {# templates/blog/layout.html.twig #}
  2. {% extends 'base.html.twig' %}
  3.  
  4. {% block body %}
  5. <h1>Blog</h1>
  6.  
  7. {% block content %}{% endblock %}
  8. {% endblock %}

The template extends from base.html.twig and only defines the contents ofthe body block. The rest of the parent template blocks will display theirdefault contents. However, they can be overridden by the third-level inheritancetemplate, such as blog/index.html.twig, which displays the blog index:

  1. {# templates/blog/index.html.twig #}
  2. {% extends 'blog/layout.html.twig' %}
  3.  
  4. {% block title %}Blog Index{% endblock %}
  5.  
  6. {% block content %}
  7. {% for article in articles %}
  8. <h2>{{ article.title }}</h2>
  9. <p>{{ article.body }}</p>
  10. {% endfor %}
  11. {% endblock %}

This template extends from the second-level template (blog/layout.html.twig)but overrides blocks of different parent templates: content fromblog/index.html.twig and title from base.html.twig.

When you render the blog/index.html.twig template, Symfony uses threedifferent templates to create the final contents. This inheritance mechanismboosts your productivity because each template includes only its unique contentsand leaves the repeated contents and HTML structure to some parent templates.

Read the Twig template inheritance docs to learn more about how to reuseparent block contents when overriding templates and other advanced features.

Output Escaping

Imagine that your template includes the Hello {{ name }} code to display theuser name. If a malicious user sets <script>alert('hello!')</script> astheir name and you output that value unchanged, the application will display aJavaScript popup window.

This is known as a Cross-Site Scripting (XSS) attack. And while the previousexample seems harmless, the attacker could write more advanced JavaScript codeto performs malicious actions.

To prevent this attack, use "output escaping" to transform the characterswhich have special meaning (e.g. replace < by the &lt; HTML entity).Symfony applications are safe by default because they perform automatic outputescaping thanks to the Twig autoescape option:

  1. <p>Hello {{ name }}</p>
  2. {# if 'name' is '<script>alert('hello!')</script>', Twig will output this:
  3. '<p>Hello &lt;script&gt;alert(&#39;hello!&#39;)&lt;/script&gt;</p>' #}

If you are rendering a variable that is trusted and contains HTML contents,use the Twig raw filter to disable the output escaping for that variable:

  1. <h1>{{ product.title|raw }}</h1>
  2. {# if 'product.title' is 'Lorem <strong>Ipsum</strong>', Twig will output
  3. exactly that instead of 'Lorem &lt;strong&gt;Ipsum&lt;/strong&gt;' #}

Read the Twig output escaping docs to learn more about how to disable outputescaping for a block or even an entire template.

Template Namespaces

Although most applications store their templates in the default templates/directory, you may need to store some or all of them in different directories.Use the twig.paths option to configure those extra directories. Each path isdefined as a key: value pair where the key is the template directory andthe value is the Twig namespace, which is explained later:

  • YAML
  1. # config/packages/twig.yaml
  2. twig:
  3. # ...
  4. paths:
  5. # directories are relative to the project root dir (but you
  6. # can also use absolute directories)
  7. 'email/default/templates': ~
  8. 'backend/templates': ~
  • XML
  1. <!-- config/packages/twig.xml -->
  2. <container xmlns="http://symfony.com/schema/dic/services"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:twig="http://symfony.com/schema/dic/twig"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/services
  6. https://symfony.com/schema/dic/services/services-1.0.xsd
  7. http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
  8.  
  9. <twig:config>
  10. <!-- ... -->
  11. <!-- directories are relative to the project root dir (but you
  12. can also use absolute directories -->
  13. <twig:path>email/default/templates</twig:path>
  14. <twig:path>backend/templates</twig:path>
  15. </twig:config>
  16. </container>
  • PHP
  1. // config/packages/twig.php
  2. $container->loadFromExtension('twig', [
  3. // ...
  4. 'paths' => [
  5. // directories are relative to the project root dir (but you
  6. // can also use absolute directories)
  7. 'email/default/templates' => null,
  8. 'backend/templates' => null,
  9. ],
  10. ]);

When rendering a template, Symfony looks for it first in the twig.pathsdirectories that don't define a namespace and then falls back to the defaulttemplate directory (usually, templates/).

Deprecated since version 4.2: Symfony looks for templates in the src/Resources/views/ too beforefalling back to the default directory. But that behavior is deprecated sinceSymfony 4.2 and will be removed in Symfony 5.0.

Using the above configuration, if your application renders for example thelayout.html.twig template, Symfony will first look foremail/default/templates/layout.html.twig and backend/templates/layout.html.twig.If any of those templates exists, Symfony will use it instead of usingtemplates/layout.html.twig, which is probably the template you wanted to use.

Twig solves this problem with namespaces, which group several templatesunder a logic name unrelated to their actual location. Update the previousconfiguration to define a namespace for each template directory:

  • YAML
  1. # config/packages/twig.yaml
  2. twig:
  3. # ...
  4. paths:
  5. 'email/default/templates': 'email'
  6. 'backend/templates': 'admin'
  • XML
  1. <!-- config/packages/twig.xml -->
  2. <container xmlns="http://symfony.com/schema/dic/services"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:twig="http://symfony.com/schema/dic/twig"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/services
  6. https://symfony.com/schema/dic/services/services-1.0.xsd
  7. http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
  8.  
  9. <twig:config>
  10. <!-- ... -->
  11. <twig:path namespace="email">email/default/templates</twig:path>
  12. <twig:path namespace="admin">backend/templates</twig:path>
  13. </twig:config>
  14. </container>
  • PHP
  1. // config/packages/twig.php
  2. $container->loadFromExtension('twig', [
  3. // ...
  4. 'paths' => [
  5. 'email/default/templates' => 'email',
  6. 'backend/templates' => 'admin',
  7. ],
  8. ]);

Now, if you render the layout.html.twig template, Symfony will render thetemplates/layout.html.twig file. Use the special syntax @ + namespace torefer to the other namespaced templates (e.g. @email/layout.html.twig and@admin/layout.html.twig).

Note

A single Twig namespace can be associated with more than one templatedirectory. In that case, the order in which paths are added is importantbecause Twig will start looking for templates from the first defined path.

Bundle Templates

If you install packages/bundles in your application, theymay include their own Twig templates (in the Resources/views/ directory ofeach bundle). To avoid messing with your own templates, Symfony adds bundletemplates under an automatic namespace created after the bundle name.

For example, the templates of a bundle called AcmeFooBundle are availableunder the AcmeFoo namespace. If this bundle includes the template<your-project>/vendor/acmefoo-bundle/Resources/views/user/profile.html.twig,you can refer to it as @AcmeFoo/user/profile.html.twig.

Tip

You can also override bundle templates in caseyou want to change some parts of the original bundle templates.

Learn more