Aller au contenu principal

Les délimiteurs Twig : ce problème d'espace blanc que vous ignorez

Gaps inline-block, diffs bruyants, layouts instables : comprenez l'impact des délimiteurs Twig sur l'espace blanc et adoptez les bonnes pratiques avec {%- et {{-.
Catégorie

DX

Marre des environnements de dev frustrants et des outils qui ralentissent votre workflow ? Améliorez votre quotidien avec des méthodes qui remettent enfin le plaisir au cœur du code.

Lecture
8 min
Niveau
Intermédiaire
janv 31 2026
Partager

Pendant des années, j'ai développé des applications Symfony sans jamais remettre en question les fondamentaux. Les templates fonctionnaient, le HTML s'affichait, tout semblait sous contrôle jusqu'à ce que je commence à remarquer des espaces en inspectant le code source des pages : pas ceux visibles dans mon IDE, mais ceux qui s'accumulent silencieusement entre les balises, créant des marges imprévues, cassant les alignements et gonflant mes diffs à chaque refactoring.

Le déclic est survenu pendant la construction d'une barre de navigation avec des icônes en inline-block. Des gaps mystérieux apparaissaient entre les éléments malgré un markup apparemment propre. C'est à ce moment que j'ai compris : Twig générait du HTML avec des sauts de ligne et des espaces que je n'avais jamais consciemment contrôlés. J'avais un moteur de templating moderne entre les mains, mais je pilotais à l'aveugle sur la production du markup final.

Cette friction a transformé ma façon d'écrire des templates. J'ai cessé de traiter mes fichiers Twig comme de simples squelettes et j'ai commencé à les lire comme du code de production. Au cœur de ce changement, trois délimiteurs : {% %}, {{ }}, et {# #}. Tout le monde connaît leur syntaxe. Peu de développeurs maîtrisent leur impact sur l'espace blanc, et c'est précisément ce qui différencie un rendu propre d'un markup fragile.

Pourquoi c'est plus critique qu'il n'y paraît

Twig n'est pas qu'un langage de templating : c’est un compilateur. Chaque template devient du PHP, chaque tag devient une instruction, chaque saut de ligne devient une partie intégrante de la sortie HTML. Tant que j'ignore ce flux de production, je rate un niveau de contrôle fondamental. Les conséquences sont mesurables :

  • La stabilité des layouts se dégrade, particulièrement avec inline-block et certaines configurations flexbox qui interprètent l'espace blanc comme du contenu réel
  • La taille des réponses HTTP augmente à cause de caractères invisibles accumulés
  • Les diffs Git deviennent bruyants et difficiles à reviewer lors des refactorings
  • Les tests de snapshot HTML échouent sur des changements d'espaces qui semblent anodins

La propreté de mon HTML ne dépend pas seulement du CSS : elle dépend de ma compréhension des délimiteurs Twig. La bonne nouvelle : Twig fournit des outils puissants pour contrôler ce comportement, mais ils restent discrets dans la documentation de base. L'ajout d'un simple - modifie radicalement le flux de sortie. Une fois ce mécanisme assimilé, tout devient cohérent.

Comment les délimiteurs fonctionnent réellement

Je me suis contraint à penser selon trois catégories distinctes :

  1. Exécution : {% %} exécute du code mais n'écrit rien en sortie.
  2. Affichage : {{ }} exécute du code et l'inscrit dans la sortie.
  3. Commentaire : {# #} n'existe pas à la compilation.

⠀J'ai ensuite établi une règle visuelle : l'espace blanc appartient au délimiteur, pas à la ligne qui le précède ou le suit. Ce que je tape entre les tags semble innocent dans l'éditeur, mais se transforme en caractères réels dans le HTML final. Twig offre un contrôle granulaire via le modificateur - :

  • {{- et -}} suppriment l'espace blanc autour des expressions affichées
  • {%- et -%} suppriment l'espace blanc autour des blocs de code
  • {#- et -#} suppriment l'espace blanc autour des commentaires

Dans le contexte Twig, "espace blanc" désigne précisément les blancs immédiatement adjacents : espaces, tabulations et sauts de ligne. Cette définition technique change tout. Je maintiens également un autre principe : la sortie finale doit rester lisible dans "afficher le code source" du navigateur.

Du code de production, pas des exemples académiques

Prenons un cas réel issu de mon travail quotidien : une page de blog listant des articles avec tags, métadonnées et bouton de lecture. Le template est structuré en composants Twig modulaires pour rester maintenable à long terme.

Un include qui injecte des espaces invisibles

Twig
{# templates/content/post_list.html.twig #}
<ul class="PostList">
###     {% for post in posts %}
###         <li class="PostList-item">
###             {% include "content/_post_card.html.twig" with { post: post } %}
###         </li>
###     {% endfor %}
### </ul>

Ce template semble irréprochable, mais l'include génère systématiquement un saut de ligne avant et après son contenu. Si mon _post_card.html.twig commence par une balise <article>, le HTML final contient des espaces entre <li> et <article>. Généralement inoffensif — jusqu'au moment où je bascule vers un design compact avec display: inline-block. Ces espaces deviennent alors visuellement perceptibles.

La correction est simple mais intentionnelle :

Twig
<ul class="PostList">
	{%- for post in posts %}
		<li class="PostList-item">
			{{- include("content/_post_card.html.twig", { post: post }) -}}
		</li>
	{%- endfor %}
</ul>

Ici, je suis explicite dans mon intention : la boucle ne doit introduire aucun blanc superflu autour des items, et l'include doit adhérer directement au <li>. C'est un choix architectural, pas un hack de dernière minute.

La liste d'icônes qui génère des gaps parasites

Twig
{# templates/content/_post_meta.html.twig #}
<div class="PostMeta">
	<a class="PostMeta-item" href="{{ path('content_category_show', { slug: post.category.slug }) }}">
	{{ post.category.name }}
</a>
<span class="PostMeta-sep">•</span>
	<a class="PostMeta-item" href="{{ path('content_tag_show', { slug: tag.slug }) }}">
		{{ tag.name }}
	</a>
</div>

Lorsque je configure ces liens en inline-block pour aligner des icônes, les sauts de ligne se transforment en espaces visibles. Je pourrais contourner ce problème en CSS avec font-size: 0 sur le conteneur, mais je préfère corriger à la source. Une première approche consiste à tout mettre sur une seule ligne :

Twig
<div class="PostMeta">
	<a class="PostMeta-item" href="{{ path('content_category_show', { slug: post.category.slug }) }}">
		{{ post.category.name }}</a><span class="PostMeta-sep">•</span><a class="PostMeta-item" href="{{ path('content_tag_show', { slug: tag.slug }) }}">
		{{ tag.name }}
	</a>
</div>

Certes, ça fonctionne, mais le markup devient illisible. Pour conjuguer les deux impératifs — contrôle des espaces et lisibilité — je combine les modificateurs - avec une approche plus subtile :

Twig
<div class="PostMeta">
	<a class="PostMeta-item" href="{{- path('content_category_show', { slug: post.category.slug }) -}}">
		{{- post.category.name -}}
	</a>{#- #}
	<span class="PostMeta-sep">•</span>{#- #}
	<a class="PostMeta-item" href="{{- path('content_tag_show', { slug: tag.slug }) -}}">
		{{- tag.name -}}
	</a>
</div>

Le commentaire {#- #} constitue une arme secrète. Il supprime l'espace blanc environnant sans introduire de bruit visuel dans le HTML final. Je l'utilise comme un joint invisible entre les éléments adjacents.

Maintenir la propreté des classes dynamiques

Je génère fréquemment des classes CSS dynamiques dans Twig. Le rendu peut rapidement devenir chaotique si je laisse des sauts de ligne s'accumuler sans contrôle.

Twig
{# templates/content/_post_card.html.twig #}
{% set classes = [
	"PostCard",
	post.isFeatured ? "is-featured" : null,
	post.readingTime > 6 ? "is-longread" : null,
] | filter(v => v is not null) | join(" ") %}

<article class="{{ classes }}">
	<h2 class="PostCard-title">
		<a href="{{ path('content_post_show', { slug: post.slug }) }}">
			{{ post.title }}
		</a>
	</h2>
	<p class="PostCard-excerpt">
		{{ post.excerpt }}
	</p>
</article>

Le bloc {% set %} ne produit aucune sortie directe, mais la ligne vide entre ce bloc et l'article peut générer un saut de ligne dans le HTML final. Cela reste acceptable dans la majorité des cas, mais pour un rendu compact, j'emploie -%} :

Twig
{% set classes = [
	"PostCard",
	post.isFeatured ? "is-featured" : null,
	post.readingTime > 6 ? "is-longread" : null,
] | filter(v => v is not null) | join(" ") -%}

<article class="{{ classes }}">
	<h2 class="PostCard-title">
		<a href="{{ path('content_post_show', { slug: post.slug }) }}">
			{{- post.title -}}
		</a>
	</h2>
	<p class="PostCard-excerpt">
		{{ post.excerpt }}
	</p>
</article>

J'élimine le saut de ligne après le set et je contrôle précisément l'espacement autour du titre. Ce sont des micro-décisions, mais elles rendent mon HTML prévisible et reproductible.

Éviter les doubles sauts de ligne dans les composants

Lorsque je rends un composant Twig dans un layout parent, j'évite les "lignes blanches doubles" générées par l'empilement de blocs :

Twig
{# templates/base.html.twig #}
<main class="Layout">
	{% block body %}{% endblock %}
</main>

Si le bloc body démarre par une ligne vide dans un template enfant, j'obtiens plusieurs sauts de ligne consécutifs dans la sortie. Ma solution habituelle :

Twig
<main class="Layout">
	{%- block body %}{% endblock -%}
</main>

Cette approche garantit que le contenu du body adhère directement au <main> sans espace superflu. Je réserve cette technique aux zones critiques — heros, grilles, icônes inline — plutôt que de l'appliquer systématiquement partout.

Les décisions de design que j'ai adoptées

L'utilisation du - n'est pas systématique, sinon les templates deviendraient de véritables casse-têtes. Quatre principes guident ma pratique :

  • Supprimer les espaces là où ils perturbent le rendu visuel. Éléments inline-block, icônes, barres de navigation compactes. Un template un peu plus dense vaut mieux qu'une mise en page instable en production.
  • Conserver les retours à la ligne quand la lisibilité du code compte. Blocs éditoriaux, paragraphes, listes. Le HTML doit rester intelligible lors d'une inspection directe dans le navigateur ou les outils de debug.
  • Faire du - un marqueur d'intention délibérée. En relisant {%- ou {{-, on comprend immédiatement qu'un développeur a réfléchi à l'espace blanc — ce n'est pas un hasard.
  • Utiliser les commentaires Twig comme levier technique. Invisibles en production, ils permettent d'éliminer les blancs sans alourdir le markup ni affecter les performances.

La sécurité reste omniprésente :{{ }} échappe automatiquement le contenu — protection essentielle contre les injections XSS. Dès qu'un filtre |raw apparaît, je m'interroge : ce HTML est-il maîtrisé et assaini en amont ? Sinon, passage obligatoire par un service de sanitization. Soigner la gestion des espaces n'a aucun sens si cela introduit des failles critiques.

La conclusion

À mes débuts avec Twig, je voyais {% %} et {{ }} comme de simples variantes syntaxiques sans réelle portée. Avec l'expérience, ma perception a changé : ce sont des leviers architecturaux qui façonnent le rendu. Le modificateur - est devenu un outil discret mais décisif pour le design d'interface. Il n'altère pas la logique métier mais affine l'intention et la précision du HTML produit.

Ce n'est pas une coquetterie de perfectionniste : c’est ce qui distingue un template qui « tourne » d'un template qui dure car il est capable d'absorber les évolutions du design system et les refactorings à venir.

Dans Twig, l'espace blanc relève d'un choix architectural délibéré, pas d'un accident. Je préfère assumer ce choix explicitement, ligne après ligne, avec des délimiteurs qui endossent pleinement leur responsabilité sur la qualité du rendu final.

Poursuivre la lecture

Sélectionné avec soin pour vous.

PHP

Les DTOs : arrêtez de transformer vos données en kebab

Découvrez pourquoi les DTOs sont essentiels pour des données bien rangées. Promis, pas de sauce blanche dans le code !

4 min de lecture
Tests

Les tests : comment bien jouer au Cluedo

Comprendre enfin la différence entre Mock et Functional Test. Un guide pratique sur PHPUnit basés sur des cas réels.

4 min de lecture
PHP

PHP 8.4 : rattrapage de retard ou véritable modernisation ?

PHP 8.4 introduit les Property Hooks, la visibilité asymétrique et un parser HTML5 natif. Découvrez les nouveautés majeures de cette version attendue depuis des années.

4 min de lecture