Le dev simple, efficace et élégant.

Les bases d'Eleventy à travers un exemple simple

Dernière édition :

Table des matières

Dans l'article précédent, nous avons vu comment initialiser un nouveau projet Eleventy à partir de zéro, et y ajouter des plugins. Nous allons maintenant construire notre projet de mini-blog au moyen de templates.

Cet exemple simple, dont le code est téléchargeable (cf. lien un peu plus bas), te permettra de découvrir les concepts de base d'un projet Eleventy :

À quoi ressemblera notre blog ?

Notre blog simplifié sera composé des pages suivantes :

Voici un aperçu de la page d'accueil sur mobile :

eleventy-sample-project_mobile-view

Dans chaque page, on retrouvera :

Note

Les sources de ce projet sont disponibles dans ce référentiel GitHub.

Les 3 types de sources d'un projet

Les sources du projet sont constituées des fichiers qui servent à générer les pages HTML finales du site. Elles sont de 3 types :

Les fichiers de templates utilisent un langage de templating et peuvent intégrer des métadonnées YAML et du code HTML, CSS et JavaScript. On distingue 2 types de templates :

Les fichiers de données contiennent typiquement les métadonnées du site (titre, slogan, URL de déploiement, description de l'auteur, etc.) ou des données communes à un ensemble de templates.

Les fichiers images permettent d'illustrer les pages. Dans l'article précédent, nous avons vu que le plugin @11ty/eleventy-img permettait de gérer facilement l'encodage, la taille et le chargement des images.

Organisation du projet

Arborescence de dossiers et fichiers

Voici un exemple d'organisation des différents types de fichiers dans le projet :

Dossiers et fichiers d'un projet 11ty

Remarque : les icônes et couleurs des dossiers et fichiers sont définies automatiquement par le plugin vscode-icons de VS Code.

À la racine du projet, on trouve le fichier de configuration, les fichiers de liste des packages, et la page d'accueil (index.njk). Tous les autres fichiers sont rangés dans les dossiers suivants :

Dossier Contenu
_data fichiers de données
_includes\layouts templates de mise en page
_site fichiers finaux générés par Eleventy à partir des sources
blog articles du blog (fichiers Markdown)
img images
node_modules packages utilisés par Eleventy
pages pages générales du site, hors page d'accueil

Note

Tous les noms de dossiers qui commencent par un underscore sont des noms par défaut utilisés par Eleventy. On peut choisir d'autres noms, mais il faut dans ce cas les préciser dans le fichier de configuration.

Le dossier _includes contient des fichiers utilisés dans d'autres templates : templates de disposition, composants, fichiers CSS... On peut organiser son contenu en sous-dossiers nommés par exemple layouts, components et css pour mieux s'y retrouver.

Les dossiers blog, img et pages ne sont pas obligatoires, mais permettent également de s'y retrouver facilement dans les sources.

Gestion des images

La centralisation des images dans un dossier img présente 2 gros avantages :

Cependant, lorsqu'on crée un blog et que les images des articles ne sont pas partagées entre articles, cette centralisation ne permet pas de voir facilement quelles images sont utilisées dans un article. Mais voici une astuce pour pallier cet inconvénient :

Tip

En préfixant chaque nom d'image par le nom (=URL) de l'article, les images d'un même article son regroupées et donc faciles à retrouver dans le dossier img. De plus, des noms explicites favorisent le SEO.

Voici un exemple des noms d'images de mon blog :

eleventy-sample-project_image-files

Chaque groupe représente un article ou une page.
Chaque nom d'image suit la nomenclature url-article_nom-image.ext. Si un article ne contient qu'une image, je peux éventuellement omettre le nom de l'image.

Tip

Comme nous l'avons vu dans l'article précédent, le plugin @eleventy-img peut encoder automatiquement toutes les images en AVIF pour réduire leur poids de façon substantielle et gérer leur chargement tardif. Au départ, j'encodais mes images PNG en AVIF dans Paint.Net, mais je me suis aperçu que le plugin d'Eleventy était bien plus rapide, alors je le laisse faire et ça m'économise beaucoup de temps 😊.

Gestion des styles

Il existe plusieurs façons plus ou moins complexes de gérer les styles dans un projet Eleventy. Ma méthode préférée, valable pour un code CSS pas trop volumineux, est d'intégrer le CSS dans les balises <style> des fichiers templates. Ainsi :

Cette logique s'inspire des architectures à base de composants. Elle est très intuitive (on sait où placer les différents styles), évite les conflits, et simplifie la prise en main et la maintenance du code.

Note

Il n'y a donc aucun fichier CSS dans notre arborescence de fichiers, car tous les styles sont intégrés dans les templates. Je n'ai pas intégré le code CSS dans les extraits de code de cet article, car ce serait trop verbeux, mais je t'invite vraiment à télécharger les sources et à regarder la structure du code CSS.

Dossier de sortie

Le dossier _site contient les fichiers finaux du site à déployer sur le serveur. Son contenu reflète en partie l'arborescence des sources. On y retrouve par exemple les dossiers blog, img et pages :

Les dossiers blog et pages contiennent un sous-dossier pour chaque page du site, sauf pour la page index.html qui doit rester à la racine. En effet, Eleventy adopte la convention recommandée par le W3C, qui consiste à placer chaque page dans un dossier contenant un fichier index et éventuellement des fichiers images, css et js spécifiques à cette page.

Dans notre exemple, les sous-dossiers ne contiennent que des fichiers index.html, car les images sont centralisées dans le dossier img, et les styles CSS sont centralisés dans les templates.

Tip

Cette convention du W3C permet d'éviter les liens directs vers les fichiers finaux. En effet, chaque page est accessible par une URL qui s'arrête au nom du dossier (ex. : https://develegant.com/fr/pages/about/), et le serveur renvoie par défaut le fichier index.* qu'il trouve dans ce dossier. Ceci laisse la possibilité de modifier l'extension du fichier index sans conséquence sur son URL.

Les templates

Template de base

Pour décrire la disposition générale du site (en-tête, corps et pied de page), nous créons le template _include/layout/base.njk (l'extension fait référence à Nunjucks) avec le contenu suivant :


<!doctype html>
<html lang="{{ metadata.language }}">
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>{{ metadata.title }}</title>
		<meta name="description" content="{{ description or metadata.description }}">
<style>
...
</style>

	</head>
	<body>
		<header id="titleBar">
         <h1 id="siteTitle"><a href="/">{{ metadata.title }}</a></h1>
         <p id="slogan">{{ metadata.slogan }}</p>
		</header>

		<main>
			{{ content | safe }}
		</main>

		<footer>
			<p>Site built with <a href="https://www.11ty.dev/">{{ eleventy.generator }}</a></</p>
         <p><a href="/pages/about">About me</a></p>
		</footer>
	</body>
</html>

La syntaxe `` est une syntaxe de template qui permet de récupérer la métadonnée title qui se trouve dans le fichier _data/metadata.json, dont voici le contenu :

{
	"title": "Demo Blog",
	"slogan": "Build your static website with 11ty !",
	"language": "fr",
	"url": "https://localhost:8080",
	"author": {
		"name": "Cyril Seguenot",
		"email": "contact@develegant.com"
	}
}

La ligne `` fait référence au contenu de la page qui utilise ce template. Par exemple, les templates de la page d'accueil et de la page "À propos", qui utilisent ce template de base, intégreront chacun leur contenu spécifique à la place de cette ligne.
safe indique simplement que ce contenu est sûr et n'a pas besoin d'échappement des caractères spéciaux.

Layouts hérités

L'héritage de template, que j'ai présenté en détail dans cet article, est une technique puissante pour créer une hiérarchie de layouts. Ainsi en faisant hériter le layout post.njk du layout base.njk, on peut ajouter des éléments spécifiques aux articles tout en héritant de la disposition de base.

Voici un extrait du code de ce template :


---
layout: layouts/base.njk
---
<style>
	/* styles spécifiques aux articles */
</style>

<article>
  <header>
    <h1>{{ title }}</h1>
    {% if page.date %}
    <p class="post-meta">
      Published on <time datetime="{{ page.date.toISOString() }}">{{ page.date.toLocaleDateString() }}</time>
    </p>
    {% endif %}
   </header>
  {{ content | safe }}
</article>

Les 3 premières lignes de code forment le frontmatter écrit en YAML. C'est là qu'on déclare que le template hérite son layout du template de base.

Note

Eleventy recherche automatiquement les layouts dans le dossier _include. C'est pourquoi l'on spécifie un chemin relatif par rapport à ce dossier.

La syntaxe d'héritage ci-dessus n'est pas propre à Nunjucks, mais à Eleventy. En utilisant un frontmatter en YAML, Eleventy permet d'avoir une syntaxe homogène et indépendante du langage de template, y compris pour les langages qui n'implémentent pas nativement la notion d'héritage, comme le Markdown.

Templates finaux

Dans la hiérarchie des templates, on trouve en bout de chaîne les articles de blog et les pages générales du site. Ces fichiers comportent aussi un frontmatter YAML.

Voici par exemple le frontmatter d'un article :

---
layout: layouts/post.njk
title: Static vs Dynamic Sites
description: The differences between static and dynamic sites, and when to use each.
date: 2025-08-05
---

On déclare tout d'abord le fichier de disposition à utiliser, puis on définit les métadonnées de l'article : son titre, sa description courte et sa date d'édition.
Eleventy récupère automatiquement ces données et les stocke dans une collection en mémoire. On pourra ainsi les récupérer pour afficher les extraits d'articles sur la page d'accueil.

Et voici le frontmatter de la page A propos :

---
layout: layouts/base.njk
title: About
---

Voyons maintenant le template de la page d'accueil, c'est-à-dire le fichier index.njk situé à la racine du site.


---
layout: layouts/base.njk
---
<style>
   /* styles spécifiques */
</style>

<ul class="post-list">
   {%- for post in collections.posts -%}
   <li class="post-list-item">
      <a href="{{ post.url }}" class="post-list-link">{{ post.data.title }}</a>
      <p>{{ post.data.description }}</p>
      <div class="post-list-meta">
         Publié le : <time datetime="{{ post.data.date.toISOString() }}">{{ post.data.date.toLocaleDateString() }}</time>
      </div>
   </li>
   {%- endfor -%}
</ul>

collections.posts représente la collection des articles. Elle est déclarée dans le fichier de configuration, comme nous allons le voir plus loin. Cette collection donne accès aux métadonnées des articles à travers leur propriété data. On peut ainsi récupérer le titre, la description et la date des articles déclarés dans leur frontmatter YAML.

Voici le code utilisé dans post.njk pour construire les liens vers les articles précédent et suivant au bas de chaque article :


{% set prevnextPosts = collections.posts | getPrevNext(page.url) %}
{% if prevnextPosts.prev or prevnextPosts.next %}
<nav class="post-navigation">
  <div>
    {% if prevnextPosts.prev %}
      <a href="{{ prevnextPosts.prev.url }}">
        <span>Previous post</span>
        <strong>{{ prevnextPosts.prev.data.title }}</strong>
      </a>
    {% endif %}
  </div>
  <div class="post-navigation-next">
    {% if prevnextPosts.next %}
      <a href="{{ prevnextPosts.next.url }}">
        <span>Next post</span>
        <strong>{{ prevnextPosts.next.data.title }}</strong>
      </a>
    {% endif %}
  </div>
</nav>
{% endif %}

La première ligne applique un filtre getPrevNext (cf. définition plus bas) sur la collection des articles pour récupérer l'article qui précède et l'article qui suit. On passe l'url de l'article courant en paramètre du filtre, et on stocke le résultat du filtre dans une variable prevnextPosts qui donne accès aux articles précédent et suivant s'ils existent.

page est une variable renseignée automatiquement par Eleventy, qui fait référence à la page courante. Elle représente un gros objet qui contient toutes les propriétés et le contenu de la page, notamment la propriété data qui contient ses métadonnées.

Le fichier de configuration

Un projet Eleventy comporte obligatoirement un fichier eleventy.config.js qui décrit sa configuration. C'est en quelque sorte le centre névralgique d'Eleventy. Il contient notamment :

Voici le code du fichier de configuration de notre exemple :

// Import functions of external libraries
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
import path from "node:path";

// Core configuration function of 11ty
export default async function (eleventyConfig) {
	// --- ADD PLUGINS and configure it ---

	// Config for the @11ty/eleventy-img plugin
	eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
		// see previous article 
	});

	// --- FILTERS ---

	// Returns articles preceding and following the url passed as parameter
	eleventyConfig.addFilter("getPrevNext", (collection, url) => {
		const pageIndex = collection.findIndex(item => item.url === url);

		if (pageIndex === -1)
			return { prev: null, next: null };

		// The collection is sorted from most recent to oldest.
		// "next" is the element with a lower index (most recent).
		// "prev" is the element with a higher (older) index.
		const next = pageIndex > 0 ? collection[pageIndex - 1] : null;
		const prev = pageIndex < collection.length - 1 ? collection[pageIndex + 1] : null;

		return { prev, next };
	});

	// --- COLLECTIONS ---

	// Collection of articles built from the Markdown files stored in the "blog" folder
	eleventyConfig.addCollection("posts", function (collectionApi) {
		const posts = collectionApi.getFilteredByGlob("./blog/*.md").sort((a, b) => b.date - a.date);
		//console.log(posts.length, "articles");
		return posts;
	});

	// --- CONFIGURATION OF TEMPLATES ENGINES ---
	return {
		dataDeepMerge: true,
		// Pre-process *.md files with: (default: `liquid`)
		markdownTemplateEngine: "njk",
		// Pre-process *.html files with: (default: `liquid`)
		htmlTemplateEngine: "njk",
		templateFormats: ["md", "njk", "html"],
	};
};

Nous avons déjà vu le début du fichier et la configuration du plugin de gestion d'images dans l'article précédent, je ne vais pas donc revenir dessus.

Dans la partie FILTERS, la méthode addFilter de l'objet de configuration permet de déclarer une fonction de filtre utilisable dans les templates, pour extraire ou transformer des infos issues des pages ou collections.
Ici, on crée un filtre getPrevNext qui extrait et renvoie les articles précédent et suivant l'article dont l'URL est passée en paramètre, dans l'ordre de la collection des articles. Grâce à ce filtre, on peut construire les liens "Article précédent" et "Article suivant" qui figurent au bas de chaque article (cf. plus haut).

Dans la partie COLLECTIONS, on crée la collection des articles (nommée posts) à l'aide de la méthode addCollection. On utilise pour cela un objet passé en paramètre et renseigné automatiquement par Eleventy, qui donne accès à l'API de gestion des collections. On utilise cette API pour créer une collection (un tableau) à partir des fichiers Markdown présents dans le dossier blog, et on trie ce tableau par date décroissante (date lue dans le frontmatter de chaque article).

Remarque : dans la syntaxe "./blog/*.md", le point désigne le dossier courant, en l'occurrence le dossier racine du site. Il est obligatoire, mais je ne sais pas pourquoi.

Enfin, la partie CONFIGURATION OF TEMPLATES ENGINES permet d'indiquer quels moteurs de template on souhaite utiliser. Noter qu'Eleventy nous autorise à intégrer dans le Markdown des instructions Nunjucks ou d'autre langage de template évolués. Il faut donc lui préciser quel moteur de template utiliser en premier sur ces fichiers avant qu'il n'interprète le Markdown.
templateFormats précise également quelles extensions de fichiers Eleventy doit traiter. En l'occurrence, il traitera tous les fichiers ".md, .njk, .html".

Note

On peut donc insérer des instructions Nunjucks dans des fichiers à l'extension ".html". L'extension ".njk" permet cependant de bien montrer que le fichier contient du code Nunjucks.

Générer le site

Une fois toutes les sources créées, on peut générer le site en local. Pour cela, on utilise la commande créée dans l'article précédent. Dans le terminal de VS Code ouvert sur le dossier racine du projet, on exécute npm run start.

Si tout se passe bien, voici ce qui s'affiche :

PS S:\Code\Eleventy\Demo> npm run start

> demo@1.0.0 start
> npx @11ty/eleventy --serve --quiet

[11ty] Wrote 5 files in 0.22 seconds (v3.1.2)
[11ty] Watching…
[11ty] Server at http://localhost:8080/

Eleventy indique qu'il a écrit 5 fichiers en 0.22s. Ils s'agit des fichiers index.html des pages d'accueil et "À propos" et des 3 articles de blogs qu'on retrouve dans les sous-dossiers de _site.

Eleventy nous indique ensuite que le site est disponible à l'URL http://localhost:8080/. En faisant CTRL + clic sur cette URL, le site s'ouvre dans le navigateur par défaut.

Important

Lorsque le site est généré de cette façon, les images ne sont pas générées immédiatement, mais plutôt à la volée lorsqu'elles sont demandées par le navigateur, afin de diminuer le temps de génération initial. Pour vérifier que les images avif sont bien générées à partir des images sources, il faut exécuter la commande npm run build et regarder le contenu du dossier _site/img.

Tant que le serveur HTTP intégré d'Eleventy est actif, toute modification des fichiers sources entraîne une reconstruction complète du site. C'est pratique pour certaines mises au point, mais avant de modifier un ensemble de fichiers, il est préférable d'arrêter le serveur (CTRL+C dans le terminal), et de le relancer après avec npm run start.

Warning

Lorsqu'on renomme ou supprime des fichiers sources, les fichiers générés dans _site à partir de ces sources ne sont pas supprimés. Il faut donc les supprimer à la main avant de relancer le site. Le plus simple est de supprimer le dossier _site complet et de laisser Eleventy le régénérer .

Conclusion

En relisant cet article avec attention et en étudiant le code source du projet exemple, tu peux économiser beaucoup de temps et d'efforts pour maîtriser les premières bases d'Eleventy, car la doc officielle n'est pas forcément le meilleur endroit pour débuter.

Il te restera ensuite encore beaucoup de choses à découvrir pour exploiter pleinement le potentiel d'Eleventy, et cela demande du temps et de la patience, comme pour tout ce qui en vaut vraiment la peine 😉.

Pour ma part, je ne regrette pas du tout d'avoir passé quelques semaines à maîtriser Eleventy pour créer mon blog (même si j'ai parfois pesté contre la doc 😌), car le résultat en valait vraiment la peine. J'ai quasiment tout appris en commençant par regarder des exemples, puis en faisant de nombreux essais et en prenant beaucoup de notes. C'est la méthode qui marche le mieux pour moi.

Je vais continuer à partager mes connaissances d'Eleventy sur ce blog, alors n'hésite pas à revenir ici chaque semaine pour continuer à apprendre !

Si ce tuto t'a été utile, merci de laisser un commentaire pour favoriser son référencement. Et si tu penses qu'il peut aider d'autres personnes, n'hésite pas à le partager !