Whitespace control in Twig templates allows you to control the indentation and spacing of the generated contents (usually HTML code). Controlling white spaces is very important when using Twig to generate contents like YAML, text emails or any other format where the white spaces are significant.
In contrast, when generating HTML contents, most of the times you should ignore this feature, because the HTML contents are minified and compressed before sending them to the users, so trying to generate perfectly aligned HTML code is just a waste of time.
However, there are some specific cases where whitespace can change how things
are displayed. For example, when an <a>
element contains white spaces after
the link text and the link displays an underline, the whitespace is visible.
That's why Twig provides multiple ways of controlling white spaces. In recent
Twig versions, we've improved those features.
New whitespace trimming options
Contributed by
Fabien Potencier
in #2925.
Consider the following Twig snippet:
1 2 3 4 5 6 7
<ul>
<li>
{% if some_expression %}
{{ some_variable }}
{% endif %}
</li>
</ul>
If the value of some_variable
is 'Lorem Ipsum'
, the HTML generated when
the if
expression matches, would be the following:
1 2 3 4 5
<ul>
<li>
Lorem Ipsum
</li>
</ul>
Twig only removes by default the first \n
character after each Twig tag
(the \n
after the if
and endif
tags in the previous example). If
you want to generate HTML code with better indention, you can use the -
character, which removes all white spaces (including newlines) from the left
or right of the tag:
1 2 3 4 5 6 7
<ul>
<li>
{%- if some_expression %}
{{- some_variable -}}
{% endif -%}
</li>
</ul>
The output is now:
1 2 3
<ul>
<li>Lorem Ipsum</li>
</ul>
Starting from Twig 1.39 and 2.8.0, you have another option to control
whitespace: the ~
character (which can be applied to {{
, {%
and
{#
). It's similar to -
, with the only difference that ~
doesn't
remove newlines:
1 2 3 4 5 6 7
<ul>
<li>
{%~ if some_expression %}
{{ some_variable -}}
{% endif ~%}
</li>
</ul>
The output now contains the newlines after/before the <li>
tags, so the
generated HTML is more similar to the original Twig code you wrote:
1 2 3 4 5
<ul>
<li>
Lorem Ipsum
</li>
</ul>
Added a spaceless filter
Contributed by
Fabien Potencier
in #2872.
In previous Twig versions, there was a tag called {% spaceless %}
which
transformed the given string content to remove the white spaces between HTML
tags. However, in Twig, transforming some contents before displaying them is
something done by filters.
That's why, starting from Twig 1.38 and 2.7.3, the spaceless
tag has been
deprecated in favor of the spaceless
filter, which works exactly the same:
1
{{ some_variable_with_HTML_content|spaceless }}
However, this is commonly used with the alternative way of applying some filter to some HTML contents:
1 2 3 4 5
-{% spaceless %}
+{% apply spaceless %}
{# some HTML content here #}
-{% endspaceless %}
+{% endapply %}
In case you missed it, the apply
tag was recently added to replace the
previous filter
tag.
In any case, even after these changes, it's still recommend to not use the
spaceless
filter too much. The removal of white spaces with this filter
happens at runtime, so calling it repeatedly can hurt performance.
Fine-grained escaping on ternary expressions
Contributed by
Fabien Potencier
in #2934.
This new feature introduced in Twig 1.39 and 2.8 is not related to whitespace control, but it's an important new feature to consider in your templates. Consider the following example and the results rendered in Twig versions before 1.39 and 2.8:
1 2 3 4 5 6 7 8 9 10 11
{% set foo = '<strong>foo</strong>' %}
{% set bar = '<strong>bar</strong>' %}
{{ false ? '<strong>bar</strong>' : foo|raw }}
{# renders as '<strong>foo</strong>' #}
{{ false ? bar : foo|raw }}
{# renders as '<strong>foo</strong>' #}
{{ (false ? bar : foo)|raw }}
{# renders as '<strong>foo</strong>' #}
The reason why this example worked in that way in previous Twig versions is that
in the first ternary statement, foo
is marked as being safe and Twig does
not escape static values. In the second ternary statement, even if foo
is
marked as safe, bar
remains unsafe and so is the whole expression. The third
ternary statement is marked as safe and the result is not escaped.
This behavior was confusing to lots of designers and developers. That's why, starting from Twig 1.39 and 2.8, the result of this example has changed as follows:
1 2 3 4 5 6 7
{% set foo = '<strong>foo</strong>' %}
{% set bar = '<strong>bar</strong>' %}
{{ false ? '<strong>bar</strong>' : foo|raw }}
{{ false ? bar : foo|raw }}
{{ (false ? bar : foo)|raw }}
{# renders as '<strong>foo</strong>' in all cases #}
Before, the escaping strategy was the same for both sides of the ternary operator. Now, in Twig 1.39 and 2.8, Twig applies a special code path for ternary operator that is able to have different escaping strategies for the two sides.
Does that mean, that the priority of ternaries has been raised or the priority of the filter has been lowered?
It seems there is a typo in {{ some_variable -}}, it should be {{ some_variable ~}}.
@Alan it may look like a typo ... but I think it's correct. the only way to get the shown output is to use "-" instead of "~" in that case. I tested it with https://twigfiddle.com/ but there could be alternative solutions.
@Christian No, the priorities did not change, I've made the escaping algorithm aware of the ternary operator so now, it is able to have a different strategies for both side of the operator.