Dans la salle commune, le feu crépite.
Le tavernier déroule un parchemin : ce n’est pas la carte d’un donjon, mais la mise en page du menu.
Chaque salle, chaque porte, chaque recoin doit s’afficher sans trembler… même en pleine bataille.
Quand on bâtit une application web avec Quarkus, Qute joue ce rôle de scribe :
il assemble les fragments, injecte les données, et produit des pages claires, rapides, prévisibles.
Cet article fait le tour complet : présentation, comparaison, cas d’usage et exemples concrets.
Présentation de Qute
Qute est le moteur de templates natif de Quarkus.
Il est pensé pour la performance, la compilation à l’avance, et une intégration directe avec Java.
Dans notre taverne, les pages sont servies par un contrôleur qui expose des templates typés via @CheckedTemplate :
@CheckedTemplate(basePath = "")
public static class Templates {
public static native TemplateInstance index(String title);
public static native TemplateInstance drinks(String title, List<Drink> drinks);
public static native TemplateInstance food(String title, List<Food> food);
public static native TemplateInstance booking(String title, BookingForm form, Map<String, String> errors);
public static native TemplateInstance bookingSuccess(String title, Booking booking);
public static native TemplateInstance admin(String title, List<Booking> bookings);
}
Ici, la classe interne Templates agit comme un catalogue typé de pages.
Chaque méthode correspond à un template et définit les données attendues, ce qui rend les erreurs visibles dès la compilation.
Avec ce mécanisme, Quarkus vérifie à la compilation que les paramètres passés aux templates sont bien conformes.
On évite ainsi les fautes de frappe sur les noms de variables en production.
Qute s’appuie sur l’écosystème Quarkus sans friction.
Le module inclut les extensions dédiées au rendu de templates côté REST :
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-qute</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-qute</artifactId>
</dependency>
Ces dépendances suffisent pour brancher Qute sur des endpoints JAX-RS et rendre des pages HTML.
En clair :
quarkus-qute: moteur de templating.quarkus-rest-qute: intégration avec JAX-RS.
Qute vs Thymeleaf
Thymeleaf est un choix historique dans l’écosystème Spring.
Qute, lui, est pensé pour Quarkus et pour des builds rapides et prévisibles.
Points communs
- Ce sont deux moteurs de templates côté serveur : on prépare les données en Java et on rend du HTML.
- Ils favorisent une séparation claire entre logique métier et présentation.
- On peut réutiliser des layouts et structurer les pages avec des fragments/partials.
Points de divergence
| Critère | Qute (Quarkus) | Thymeleaf (Spring) |
|---|---|---|
| Validation | Templates vérifiés à la compilation | Validation surtout à l’exécution |
| Intégration | Modèle Java direct, peu d’abstraction | Modèle Spring centré sur le runtime |
| Performance | Très léger, peu de réflexion | Plus riche, plus de coûts runtime |
| DX | Typage fort via @CheckedTemplate |
Souple mais moins strict |
| Cas d’usage | Quarkus natif | Applications Spring |
Extension : Qute vs Vaadin
Vaadin ne joue pas dans la même cour : c’est un framework UI complet.
Il génère l’interface depuis Java (composants) et s’appuie sur un rendu côté client/serveur.
| Critère | Qute (templating) | Vaadin (UI framework) |
|---|---|---|
| Approche | HTML + données | Composants Java |
| Courbe d’apprentissage | Faible | Plus élevée |
| Contrôle HTML/CSS | Très direct | Abstraction forte |
| Cas d’usage | Pages web classiques | Applications web riches |
Quand choisir quoi ?
- Choisir Qute si l’objectif est de rendre des pages classiques (pages publiques, formulaires simples, backoffice léger) avec un contrôle fin sur le HTML/CSS.
- Choisir Vaadin si l’on vise une application web riche, type “outil interne”, avec beaucoup d’interactions et un UI piloté majoritairement en Java.
- Choisir Thymeleaf si l’écosystème Spring est déjà en place et que l’on souhaite rester sur une approche serveur classique.
Décision du tavernier
Pour les annonces, les menus, et le registre des chambres, Qute suffit largement : rapide, lisible, et fidèle au parchemin.
Pour un “tableau de bord de guilde” rempli de filtres, de drag & drop et de widgets interactifs, Vaadin prend l’avantage.
Et si la guilde est déjà jurée à Spring, Thymeleaf reste l’option la plus naturelle.
Dans notre taverne, cela se traduit par moins de surprises et un service rapide, même quand l’auberge déborde.
Exemples (templates, tags, validation, bean mapping)
Template principal avec tag personnalisé
Le layout partagé est défini dans un tag Qute (main.html).
Toutes les pages viennent s’y ancrer grâce à {#main ...} :
{#main title=title}
<p style="text-align: center; font-size: 1.3rem; line-height: 1.6;">
Bienvenue à <strong>The Falling Whale</strong>, voyageur ! <br>
Que vous veniez d'un autre royaume ou d'une autre galaxie, <br>
notre établissement est l'endroit idéal pour poser votre serviette.
</p>
{/main}
le template principal
Ce bloc montre une page “enfant” qui se contente de fournir son contenu.
Le tag main se charge d’encadrer la page et d’afficher le titre.

Le tag, lui, définit la structure globale (navigation, parchemin, footer) :
<div class="parchment">
<header>
<h1>The Falling Whale</h1>
<h2 style="font-family: 'MedievalSharp'; font-size: 1.5rem; color: #8b4513; margin-top: 0.5rem;">{title}</h2>
</header>
{nested-content}
<div class="wax-seal">42</div>
</div>
Ici, {nested-content} injecte le contenu de la page enfant dans le layout.
Le rendu final donne une page cohérente, avec un style partagé sur tout le site.
Boucles et rendu de listes
Qute s’appuie sur une syntaxe claire pour parcourir une collection.
Exemple avec la carte des boissons :
{#for drink in drinks}
<div style="margin-bottom: 2rem;">
<div style="display: flex; justify-content: space-between; font-family: 'MedievalSharp'; font-size: 1.4rem;">
<span>{drink.name}</span>
<span>{drink.price} PO</span>
</div>
<p style="font-style: italic; margin-top: 5px;">{drink.description}</p>
</div>
{/for}
La boucle #for itère sur drinks et injecte chaque boisson dans la carte.
Le rendu final donne une page lisible, avec un prix aligné à droite.

Validation côté formulaire
Le formulaire de réservation est validé via un bean dédié, annoté avec Jakarta Validation :
public class BookingForm {
@RestForm
@NotBlank(message = "Le nom est obligatoire.")
@Size(max = 100, message = "Le nom est trop long (100 caractères max).")
public String adventurerName;
@RestForm
@NotBlank(message = "La date d'arrivée est obligatoire.")
public String arrivalDate;
@RestForm
@Min(value = 1, message = "Le nombre de nuits doit être au moins 1.")
@Max(value = 10, message = "La durée du sejour ne doit pas dépasser 10 nuits.")
public int nights = 1;
@RestForm
@NotBlank(message = "Le type de suite est obligatoire.")
public String roomType;
}
Chaque champ du formulaire est annoté pour imposer les règles métier.
Le contrôleur s’appuie ensuite sur ces contraintes pour afficher des erreurs propres dans la page de réservation.
Et côté template, les messages sont rendus proprement :
{#if errors and !errors.isEmpty}
<div class="error-box">
<strong>Oups, il y a quelques problèmes :</strong>
<ul style="margin: 0.5rem 0 0 1.2rem;">
{#for e in errors.values}
<li>{e}</li>
{/for}
</ul>
</div>
{/if}
Le bloc #if affiche un encart d’erreurs uniquement si la validation a échoué.
Cela évite de polluer la page lorsqu’aucun problème n’est présent.

Bean mapping : du formulaire au modèle
Une fois validé, le formulaire est transformé en entité persistée :
Booking booking = new Booking();
booking.adventurerName = form.adventurerName;
booking.arrivalDate = parsedDate;
booking.nights = form.nights;
booking.roomType = form.roomType;
booking.persist();
Le mapping est volontairement simple : on passe du formulaire à l’entité, puis à la persistance.
Cela met en lumière le rôle de Qute : afficher les données, pas les transformer.
Le template de confirmation illustre aussi le formatage directement côté Qute :
<p>
Nous vous attendons le {booking.arrivalDate.format('dd/MM/yyyy')} pour un séjour de {booking.nights} nuits.
</p>
La date est formatée à l’affichage, sans logique métier supplémentaire.
On garde le modèle propre et la présentation claire.

Registre et branche alternative
Le registre des réservations exploite la branche #else de Qute pour gérer le cas “vide” :
{#for b in bookings}
<tr style="border-bottom: 1px dotted rgba(139, 69, 19, 0.4);">
<td style="padding: 10px;">{b.adventurerName}</td>
<td style="padding: 10px;">{b.arrivalDate.format('dd/MM/yyyy')}</td>
<td style="padding: 10px;">{b.nights}</td>
<td style="padding: 10px;">{b.roomType}</td>
</tr>
{#else}
<tr>
<td colspan="4" style="text-align: center; padding: 2rem; font-style: italic;">Le registre est vide pour l'instant.</td>
</tr>
{/for}
Si aucune réservation n’est enregistrée, le tableau affiche un message clair.
Sinon, chaque ligne montre le nom, la date formatée et la chambre choisie.


le registre des réservations
Mini lexique des directives Qute utilisées
Pour un moteur de templating, les “briques” sont les directives.
Dans ce module, on rencontre surtout :
{#main ...}et{/main}: insertion d’un template parent (tag).{nested-content}: zone où le contenu enfant est injecté.{#if condition}et{/if}: affichage conditionnel.{#for item in items}et{/for}: boucle sur une collection.{#else}: branche alternative d’une boucle ou d’une condition.{variable}: affichage d’une variable simple.{object.field}: accès à une propriété.
Exemples concrets vus dans la taverne : {#for e in errors.values}, {#for drink in drinks}, {#if errors and !errors.isEmpty}.
Ici, Qute n’est pas seul : il s’insère dans une chaîne cohérente formulaire → validation → mapping → persistance → rendu.
Erreurs fréquentes en Qute
Même dans une taverne bien tenue, il arrive que des erreurs se glissent dans les parchemins.
- Mauvais nom de variable :
drinksvsdrinkdans une boucle.
Le template compile, mais le rendu échoue si la variable n’existe pas. - Oublier
{nested-content}dans un tag : le layout s’affiche, mais le contenu disparaît. - Type inattendu dans une page : exemple,
bookingabsent ounulldans un template de confirmation. - Branche vide non gérée : oublier un
#elsedans un registre, et afficher un tableau vide sans explication.
Ces pièges sont évitables grâce à la validation à la compilation et à une structure de templates cohérente.
Bonnes pratiques pour les templates Quarkus
Dans l’esprit d’une taverne organisée, quelques règles simples évitent bien des soucis.
- Centraliser le layout dans un tag (
main.html) et garder les pages enfants minimalistes. - Limiter la logique dans les templates : Qute affiche, Java prépare.
- Préférer des modèles clairs : passer des objets dédiés (ex :
BookingForm) plutôt que desMapbrutes. - Gérer les états vides avec
#elsepour toujours afficher un message explicite. - Formater à l’affichage (dates, montants) pour garder l’entité métier propre.
Conclusion
Dans un donjon, on ne confie pas ses parchemins à un scribe maladroit.
Qute est le scribe fiable de Quarkus : rapide, précis, et parfaitement intégré à Java.
Avec ses templates vérifiés à la compilation, son système de tags, et sa proximité avec le code métier,
il permet de construire une interface web sans dénaturer l’architecture.
La taverne est prête.
Les aventuriers aussi.
Et chaque page s’affiche comme une promesse tenue.