Aller au contenu principal

Utiliser le templating Mustache avec Quarto

Maîtrisez le templating Mustache dans Quarto ! Découvrez comment utiliser l'extension quarto-partials pour créer des pages de documentation structurées et répétitives pour votre site statique.

· 8 minutes de lecture
Voir la source
Christophe
Amoureux de Markdown, WSL et Docker ~ Curieux insatiable.
Cet article existe aussi en anglais — lire la version originale.

Traduction automatique

Cette traduction a été produite par une IA. Elle peut contenir des contresens ou des maladresses, en particulier sur les termes techniques. En cas de doute, consultez l'article de référence en anglais — c'est lui qui fait foi.

Cet article a plus d'un an, mais il a été relu le 30 juillet 2026 — son contenu est toujours exact.

Utiliser le templating Mustache avec Quarto

⚡
TL;DR

Ce tutoriel explique comment utiliser un templating à la Mustache dans Quarto pour créer efficacement une documentation structurée et répétitive destinée à un site statique. Grâce à l'extension quarto-partials, vous pouvez définir des blocs de contenu réutilisables (les partials) et les inclure dans plusieurs pages. Le guide couvre la mise en place d'un projet, le passage de variables depuis le frontmatter YAML d'un document vers un partial, et l'utilisation de conditions pour afficher du contenu selon qu'une variable est définie ou non. Une approche idéale pour documenter un grand nombre de fonctionnalités similaires avec une structure cohérente.

J'utilise Quarto pour générer de la documentation ; jusqu'ici, j'ai surtout produit des fichiers .docx et .pdf.

Récemment, j'ai eu besoin de générer un site statique (HTML) permettant à mes collègues de consulter la documentation du dernier logiciel que j'ai écrit. Je devais décrire près de 80 fonctions : autant de pages web à écrire, chacune avec la même structure (un chapitre description, un chapitre comment l'exécuter, un chapitre comment le configurer, etc.). Ce besoin a fini par se transformer en le pipeline complet décrit dans Quarto - Comment j'ai construit un écosystème auto-documenté pour plus de 50 projets.

Et forcément, présenté comme ça, on pense à un CMS (content management system) comme Joomla par exemple : pouvoir définir une page standard (un template) qui contiendra les chapitres et, dans chaque chapitre, injecter du contenu sous forme de variables. Ce serait bête d'écrire chaque page à la main, non ?

C'est là qu'intervient l'idée de Mustache. Mustache se définit comme un framework de Logic-less templates.

Et comme j'utilise Quarto pour ma documentation, il me faut une extension pour utiliser Mustache : c'est Quarto-partials de Garrick Aden-Buie — une des extensions que je mentionne dans mes extensions Quarto préférées, mais elle mérite qu'on creuse bien plus loin.

Voici l'idée en une image : une page Markdown dont le chapitre « How to run » est injecté depuis un template partagé, avec une variable remplie depuis le frontmatter de la page elle-même.

Rendu de la fonctionnalité Contextual Canvas

Le titre « Contextual Canvas » et sa description sont écrits directement dans la page ; le paragraphe « How to run » en dessous provient d'un template partagé par toutes les pages de fonctionnalités, avec {{ command }} remplacé par la valeur déclarée dans le frontmatter de cette page. Construisons ça.

Créer quelques fichiers​

Comme toujours, créons quelques fichiers.

Créez un dossier temporaire et placez-vous dedans :

>user@machine: ~/yourproject

$ mkdir /tmp/partials && cd $_

Créez un fichier _quarto.yml avec ce contenu :

project:
type: website
output-dir: .
lang: en
website:
title: "Playing with Quarto partials"
page-navigation: true
bread-crumbs: true
sidebar:
style: "docked"
toc-depth: 4
contents: auto
<!-- highlight-next-line -->
filters:
<!-- highlight-next-line -->
- partials

Ce fichier configure un projet Quarto pour générer un site web. Il définit le répertoire de sortie, la langue, et les paramètres du site comme le titre, la navigation et la barre latérale. Il active aussi le filtre partials pour permettre l'utilisation de fragments de contenu réutilisables.

Ce fichier dit à Quarto que nous allons créer un site web : en lançant quarto render plus tard, nos pages écrites en Markdown seront converties en pages HTML.

Les deux dernières lignes doivent être présentes pour charger une extension externe appelée partials ; installons-la.

Comme nous utilisons Docker, la commande est plutôt longue : docker run -it --rm -v .:/public -w /public -u $(id -u):$(id -g) ghcr.io/quarto-dev/quarto:latest quarto add gadenbuie/quarto-partials.

Cela va créer un nouveau dossier _extensions contenant partials.

Découvrons les bases​

L'idée derrière Quarto-partials est de permettre d'écrire une page comme celle ci-dessous : je décris ma première fonctionnalité fictive et, dans le chapitre How to run, j'injecte le contenu d'une autre page :

---
title: Contextual Canvas
---
## Description
Imagine you're working on a complex project – perhaps writing a research paper, designing a website, or planning a marketing campaign. You have various documents open, browser tabs scattered, and maybe even some handwritten notes nearby. It can feel like juggling multiple realities at once.
*Contextual Canvas* aims to streamline this by creating a dynamic, visual workspace directly within your primary application. Instead of just seeing a static file or interface, you can summon the Canvas, which intelligently pulls in relevant information based on what you're currently working on
## How to run
{{< partial ../_partials/run.md >}}

Ce document présente Contextual Canvas, une fonctionnalité qui crée un espace de travail dynamique et visuel intégré directement dans votre application principale. Elle rassemble intelligemment les informations pertinentes en fonction de votre contexte actuel, permettant de gérer plusieurs documents et tâches sans basculer entre plusieurs fenêtres ou onglets.

Comme vous le voyez, j'écris la description de ma fonctionnalité (appelée ici « Contextual Canvas ») puis j'ai un chapitre « How to run ». Au lieu d'écrire le mode d'emploi ici, j'inclus un fichier externe (un template) appelé ../_partials/run.md.

Celui-ci utilisera l'extension Quarto-partials : je peux donc mettre dans ../_partials/run.md des fonctionnalités Mustache, comme l'injection d'une variable.

Voici le template :

In order to run this action, please run `{{ command }}`.

Existing flags:

* `--debug`: output debug information,
* `--no-color`: disable ANSI colors and
* `--verbose`: enable verbose mode; showing more information on screen

Ce document explique comment exécuter une action en ligne de commande et liste les flags optionnels disponibles pour contrôler le comportement de la commande, notamment l'affichage du débogage, la couleur et le niveau de verbosité.

Et voilà Mustache en action : comme vous le voyez sur la première ligne, la syntaxe {{ command }} affichera le contenu d'une variable appelée command. Bien sûr, je dois la déclarer. Revenons à ma documentation pour ajouter ce que Quarto appelle un bloc YAML frontmatter :

---
title: Contextual Canvas
<!-- highlight-next-line -->
partial-data:
<!-- highlight-next-line -->
command: "make canvas"
---
## Description
Imagine you're working on a complex project – perhaps writing a research paper, designing a website, or planning a marketing campaign. You have various documents open, browser tabs scattered, and maybe even some handwritten notes nearby. It can feel like juggling multiple realities at once.
*Contextual Canvas* aims to streamline this by creating a dynamic, visual workspace directly within your primary application. Instead of just seeing a static file or interface, you can summon the Canvas, which intelligently pulls in relevant information based on what you're currently working on
## How to run
{{< partial ../_partials/run.md >}}

Ce fichier de documentation décrit Contextual Canvas, une fonctionnalité qui crée un espace de travail dynamique dans votre application principale. Le fichier utilise des métadonnées frontmatter pour spécifier qu'il faut exécuter la commande 'make canvas' et inclut une explication de pourquoi cet outil aide les développeurs à gérer plusieurs documents et contextes sans basculer entre différentes fenêtres.

Il est temps de créer notre site web. Lancez docker run -it --rm -v .:/public -w /public -u $(id -u):$(id -g) ghcr.io/quarto-dev/quarto:latest quarto render — le rendu déjà aperçu en haut de cet article.

Cela va appeler la commande render de Quarto. Quarto va traiter le fichier _quarto.yml, voir que nous voulons produire un site web et lancer le rendu HTML. Ça ne prend qu'une ou deux secondes.

Maintenant, en lançant docker run -d --name partials -p 8080:80 -v .:/usr/local/apache2/htdocs/ httpd:alpine, nous créons un container Apache accessible sur le port 8080.

Enfin, rendez-vous sur http://localhost:8080/documentation/canvas.html et nous verrons notre site.

Et maintenant, nous sommes prêts à copier/coller le fichier documentation/canvas.md vers documentation/feature_2.md et à simplement mettre à jour la variable command. Et à répéter l'opération jusqu'à avoir documenté toutes les fonctionnalités.

http://localhost:8080/documentation/canvas.html
Notre fonctionnalité Canvas

Prouvons que le template est réutilisable. Créons une nouvelle fonctionnalité :

---
title: Intent-Driven Interface Builder
partial-data:
command: "make builder"
---
## Description
Imagine the process of building user interfaces. Developers and designers often spend significant time translating conceptual designs and user stories into the specific widgets, layouts, and interactions of a software application. *Intent-Driven Interface Builder* aims to revolutionize this by allowing users to describe the intent of a UI element or section, rather than manually configuring every pixel and property.
The *Intent-Driven Interface Builder* would then leverage AI and a vast library of pre-built, adaptable UI components and design patterns to automatically generate the corresponding interface elements. It would intelligently choose appropriate widgets, arrange them in a logical layout, and even suggest relevant styling based on the application's overall design language and best practices.
## How to run
{{< partial ../_partials/run.md >}}

Ce fichier de documentation décrit Intent-Driven Interface Builder, un outil qui permet aux développeurs de décrire l'intention d'un élément d'interface utilisateur plutôt que de le configurer manuellement. L'outil utilise l'IA et une bibliothèque de composants pré-construits pour générer automatiquement les éléments d'interface appropriés, y compris le choix des widgets, l'arrangement en layout et les suggestions de style selon le langage de design de l'application.

et générons à nouveau notre site en relançant docker run -it --rm -v .:/public -w /public -u $(id -u):$(id -g) ghcr.io/quarto-dev/quarto:latest quarto render.

http://localhost:8080/documentation/feature_2.html
Notre deuxième fonctionnalité

Ce que nous venons de faire​

Nous avons créé un template réutilisable. Notre document comporte deux parties : une section YAML (le frontmatter) où nous fournissons des variables. La seconde partie, celle en Markdown, est notre template de base. Nous pouvons copier celui-ci pour toutes nos fonctionnalités, par exemple.

---
partial-data:
command: "run canvas"
---

## How to run

{{< partial ../_partials/run.md >}}

Mon cas d'usage​

Dans mon cas, ma documentation ressemble à ceci :

---
title: PHP linter
categories: [linting, php]
order: 1
partial-data:
command: "lint-php"
config_file: ".phplint.yml"
config_url: "https://github.com/tengattack/phplint/blob/master/.phplint.yml"
constant: "PHPLINT"
type: "PHP"
---
<!-- cspell:ignore phplint -->
## Description
This is a brief description of the functionality. This text is hardcoded because it is different each time, from one feature to another.
## For what type of project
{{< partial ../_partials/project_type.md >}}
## How to run
{{< partial ../_partials/run.md >}}
## How to configure
{{< partial ../_partials/configure/file.md >}}
## Attention points
None.
## Remarks
None.

Ce fichier est un document de configuration pour la documentation d'un linter PHP appelé phplint. Il définit les métadonnées du projet (titre, catégories, ordre d'affichage) et les paramètres techniques (commande à exécuter, fichier de configuration, constante PHP associée), puis structure le contenu avec des sections standard expliquant son fonctionnement, sa configuration et ses points d'attention.

Tester si une variable est définie ou non (facultatif — à ignorer si vos templates n'ont pas besoin de conditions)​

Jetons un œil au fichier is_for.md.

<!-- #type means defined and thus has been set to something like "PHP" or "PYTHON" -->
{{#type}}
This job only for **{{ type }}** project.
{{/type}}
<!-- ^type means empty/missing so, in this case, the command is for all type of projects -->
{{^type}}
This stage is for **all** type of projects.
{{/type}}

Ce template utilise la syntaxe Mustache pour afficher un message différent selon que la variable type est définie ou non. Si type contient une valeur comme PHP ou PYTHON, on affiche un message spécifique à ce type de projet; sinon, on affiche un message indiquant que l'étape s'applique à tous les projets.

Deux syntaxes sont utilisées ici : {{# et {{^.

La première vérifie la présence d'une variable appelée type et, si celle-ci est définie, le bloc est traité.

La seconde s'appelle une Inverted section et vérifie l'absence de la variable : si type n'est pas définie, ce bloc sera interprété.

Si vous regardez mon fichier php_lint.md, j'ai bien une variable type définie dans ma section partial-data (celle utilisée par Quarto-partials).

---
title: PHP linter
categories: [linting, php]
order: 1
<!-- highlight-next-line -->
partial-data:
command: "lint-php"
config_file: ".phplint.yml"
config_url: "https://github.com/tengattack/phplint/blob/master/.phplint.yml"
name: "PHP Linter"
<!-- highlight-next-line -->
type: "PHP"
---
<!-- cspell:ignore phplint -->
## Description
This is a brief description of the functionality. This text is hardcoded because it is different each time, from one feature to another.
## For what type of project
{{< partial ../_partials/project_type/is_for.md >}}
## How to run
{{< partial ../_partials/run.md >}}
## How to configure
{{< partial ../_partials/configure/file.md >}}
## Attention points
None.
## Remarks
None.

Ce fichier de documentation décrit un linter PHP, qui est un outil d'analyse de code. Il configure les métadonnées du document en en-tête YAML, puis structure la documentation avec des sections standards comme description, type de projet, et instructions d'exécution et de configuration.

Si je génère mon fichier avec la ligne de commande quarto render, je verrai alors This job only for **PHP** project. dans ma documentation.

Si ma fonctionnalité concernait tous les types de projets, il me suffit de supprimer la ligne type: "PHP" de mon frontmatter. Dans ce cas, je verrai This stage is for **all** type of projects..

Jetons un œil au fichier inclus suivant :

<!-- #config_file means defined and thus there is a configuration file -->
{{#config_file}}
{{ name }} uses a settings file named .config/{{ config_file }} ([learn more]({{ config_url }})).
{{/config_file}}
<!-- ^config_file means empty/missing so no configuration file -->
{{^config_file}}
There is no configuration file.
{{/config_file}}

Ce fichier de template affiche du texte différent selon que la variable config_file existe et contient une valeur ou non. Si config_file est définie, il montre le nom du fichier de configuration et un lien pour en savoir plus. Sinon, il affiche un message indiquant qu'il n'y a pas de fichier de configuration.

Même idée. Si une clé config_file est définie dans le frontmatter de la documentation, nous obtiendrons PHP Linter uses a settings file named .config/.phplint.yml ([Learn more](https://github.com/tengattack/phplint/blob/master/.phplint.yml)).. Si nous supprimons la ligne config_file, nous obtiendrons There is no configuration file..

Contenu brut (facultatif — à ignorer sauf si une valeur affichée semble mal échappée)​

Lors de l'affichage d'une variable, il faut parfois désactiver le mode d'affichage normal et utiliser ce que Mustache appelle le raw content.

Dans l'exemple ci-dessous, j'utilise un caractère / qui sera échappé par Mustache.

partial-data:
output: ".output/coverage"
{{#output}}

The report will be created into your project's directory in a folder called `{{ output }}`.

{{/output}}

Le rendu HTML donnera ici .output&#x2F;coverage et non .output/coverage comme attendu. Comme on le voit, Mustache a échappé notre slash. Il faut donc utiliser le mode contenu brut et la syntaxe {{& output }} à la place.

{{#output}}

The report will be created into your project's directory in a folder called `{{& output }}`.

{{/output}}

Conclusion​

Une ligne d'extension dans _quarto.yml, deux ou trois templates partials, et près de 80 pages de documentation quasi identiques cessent d'être 80 fichiers Markdown copiés-collés : elles deviennent un template plus un petit bloc de frontmatter par page. Voyez Quarto - Comment j'ai construit un écosystème auto-documenté pour plus de 50 projets pour découvrir comment tout cela est devenu un pipeline complet, et mes extensions Quarto préférées pour d'autres briques du même genre.


🦋 Rejoignez la conversation sur BlueskyUn compte Bluesky gratuit est nécessaire pour aimer, repartager ou commenter. Ce bloc interroge l'API publique de Bluesky directement depuis votre navigateur (sans cookie, sans connexion) — votre adresse IP parvient donc aux serveurs de Bluesky dès que cette section se charge.

Chargement des commentaires…

Cet article vous a-t-il été utile ?
Avez-vous suivi les étapes de cet article ?