Pourquoi prendre des vacances d’été et glander sur une plage, alors qu’on pourrait pondre projet open source qui résout un vrai problème ?
Adrien Revel a fait exactement ça avec Microfolio, et le plus rigolo c’est qu’il s’est fait assister par Claude Code pour développer son bébé. Une fois installé, vous lui balancez vos fichiers dans des dossiers, vous ajoutez du Markdown pour les descriptions, et paf, vous avez un portfolio statique qui a la classe.
Pas de base de données qui rame, pas de CMS à maintenir, pas de plugins WordPress qui vous lâchent au pire moment. Juste des fichiers, du contenu, et un site qui booste.
Microfolio tourne sur SvelteKit 2, l’un des générateurs de sites statiques les plus utilisés, couplé à Tailwind CSS 4. Pour ceux qui ne suivent pas l’actu dev, SvelteKit est un framework qui compile votre code en vanilla JavaScript ultra-optimisé. Du coup, les sites chargent à la vitesse de l’éclair même sur une connexion 3G pourrie.
Maintenant, l’installation, c’est du velours. Par exemple, sous macOS, une ligne de Homebrew et c’est parti :
brew install aker-dev/tap/microfolio
microfolio new mon-portfolio
cd mon-portfolio
microfolio dev
Pour les autres OS, un bon vieux clone Git et pnpm font l’affaire. Le projet est pensé pour les designers, architectes, photographes, bref tous les créatifs qui veulent montrer leur travail sans se prendre la tête avec du code.
La démo en ligne montre déjà ce que ça donne à savoir une interface épurée, une navigation fluide entre les projets, une vue carte pour les projets géolocalisés (pratique pour les architectes), et un système de tags pour filtrer le contenu. Le tout responsive évidemment, c’est la base aujourd’hui.
Moi j’en ai fait un site de chat pour rigoler…
Pour le déploiement, GitHub Pages est supporté d’office avec possibilité de mettre un domaine custom. Adrien cherche également des beta-testeurs pour peaufiner le projet avant de sortir la v1.0 à la rentrée. Donc si vous avez un portfolio à refaire ou si vous voulez juste tester un outil moderne, c’est le moment.
Bref, du bon “file-based CMS” comme on les aime. Comme ça, au lieu de stocker vos contenus dans une base de données qui peut foirer, tout est dans des fichiers que vous pouvez versionner avec Git. Vous gardez le contrôle total sur vos données, vous pouvez tout éditer offline, et surtout vous n’êtes pas prisonnier d’un système.
Puis ce combo SvelteKit + Tailwind CSS est vraiment pertinent, je trouve. SvelteKit permet de générer des sites statiques en automatique, ce qui veut dire que votre site est servi en HTML statique pour le SEO, puis devient interactif côté client. C’est le meilleur des deux mondes.
Pour les devs qui veulent contribuer, le code est sur GitHub sous licence MIT. Le projet utilise les dernières versions de tout (SvelteKit 2, Tailwind CSS 4, Node.js 20+), donc c’est aussi l’occasion de jouer avec les dernières technos du moment.
Vous ne le savez peut-être pas, mais le site sur lequel vous êtes actuellement est un site 100% statique. Je gère le contenu côté back avec un CMS en PHP (rédaction, édition, workflow), mais la partie publique n’exécute aucun PHP : pas de base de données, juste des fichiers HTML/CSS/JS et des images. Et tout ça est généré à partir de fichiers Markdown avec Hugo.
Et optimiser tout ça c’est pas de la tarte car si vos templates sont mal pensés, Hugo peut mettre une plombe à générer le site. Entre partiels recalculés pour rien, boucles trop larges et images retraitées à chaque passage, on flingue les perfs sans s’en rendre compte.
Et c’était mon cas au début. Je regardais avec envie les mecs qui sortaient un build en moins d’une seconde tout en restant réaliste car j’ai quand même environ 60 000 pages à générer. Au final, je suis passé de plusieurs heures de build à environ 5 minutes en optimisant les templates, le cache et le pipeline d’assets.
Tout ceci est encore en cours de tests, et ce n’est pas parfait donc il est possible que ça change à l’avenir et vous faites peut-être autrement (dans ce cas ça m’intéresse !!). Bref, voici mon retour d’XP sous forme de conseils pour transformer, vous aussi, votre escargot en fusée 🚀.
Partials - Cachez-moi tout ça (bien)
Hugo sait mettre en cache le rendu d’un partial. Utilisez partialCached (alias de partials.IncludeCached) partout où la sortie est identique sur beaucoup de pages.
Le truc malin, c’est d’utiliser des variantes (clés) pour changer le cache selon le contexte. Attention quand même car ces arguments de variante ne sont pas passés au partial. En réalité, ils servent uniquement à nommer l’entrée de cache. Si vous devez passer plus de données au partial, mettez-les dans le context via un dict.
Je regroupe tout avec resources.Concat, puis minify et fingerprint (SRI + cache-busting). Le preload déclenche le chargement au plus tôt, et le link classique prend le relais.
Côté JavaScript, j’utilise js.Build (esbuild) et je bascule les sourcemaps selon l’environnement :
J’ai également une approche hybride concernant la gestion des images. J’utilise Cloudflare Image Resizing qui génère les variantes côté CDN, et un partial Hugo prépare le srcset + LCP propre.
Comme vous pouvez le voir, j’adapte la qualité selon la plateforme (85% pour mobile / 92% pour desktop), le format AVIF par défaut, et pour l’image LCP, je mets fetchpriority="high" et pas de lazy.
Cache des ressources - Le secret des rebuilds rapides
Configurez aussi les caches pour éviter de retraiter à chaque build. Le plus important c’est le cache permanent pour les images et autres assets traités.
# hugo.toml
[caches]
[caches.getJSON]
dir = ":cacheDir/:project"
maxAge = "1h"
[caches.getCSV]
dir = ":cacheDir/:project"
maxAge = "1h"
[caches.images]
dir = ":resourceDir/_gen"
maxAge = -1 # NE JAMAIS expirer
[caches.assets]
dir = ":resourceDir/_gen"
maxAge = -1
Et en cas de besoin, pour forcer un refresh : hugo server --ignoreCache.
Organisation des templates - site.RegularPages est votre ami
En régle générale, c’est mieux d’éviter de boucler sur des pages inutiles (taxonomies, terms…). Utilisez aussi site.RegularPages plutôt que .Site.Pages.
Voici également quelques réglages utiles dans mon hugo.toml :
# Désactiver ce qu'on n'utilise pasdisableKinds=["section"]# Pagination moderne (Hugo ≥ 0.128)[pagination]pagerSize=39# 39 articles par page# Traitement d'images[imaging]quality=80resampleFilter="Lanczos"# Optimisations de build[build]writeStats=falsenoJSConfigInAssets=trueuseResourceCacheWhen="always"# Mounts: exclure fichiers lourds[module][[module.mounts]]source="content"target="content"excludeFiles=["**/*.zip","**/*.log","**/backup/**","**/archives/**","**/exports/**"][[module.mounts]]source="static"target="static"excludeFiles=["**/*.zip","**/*.log","**/backup/**","**/archives/**"]
Je mets aussi un timeout large (timeout = "600s") et j’ignore certains warnings verbeux (ignoreLogs = ['warning-goldmark-raw-html']). Et pour la pagination, pagerSize a aussi remplacé les vieux réglages (bon à savoir si vous migrez).
Perso, j’utilise uniquement les flags suivants quand je lance Hugo :
Nettoyage : hugo --gc --cleanDestinationDir pour virer l’obsolète et garder public/ clean.
Dev rapide : gardez hugo server tel quel. --disableFastRender seulement si un glitch l’exige. --renderToMemory peut accélérer (au prix de la RAM).
Profiler de templates : hugo --templateMetrics --templateMetricsHints liste les templates lents et où placer vos partialCached. C’est comme ça que j’ai vu que ma sidebar coûtait une plombe.
Conditionnez ce que vous chargez
Selon la page, j’adapte aussi mon code. L’image LCP avec fetchpriority="high" (pour éviter le lazy), le reste en lazy + placeholder SVG qui respecte les dimensions (zéro layout shift). Et pour le JS, defer partout et pas de scripts inutiles sur les pages qui n’en ont pas besoin.
Le pattern qui tue - partialCached + variantes calculées
Mon combo préféré, selon le contexte c’est celui-ci :
Il faut comme ça trouver le bon équilibre. C’est à dire avoir assez de variantes pour éviter les recalculs, mais pas trop, sinon votre cache devient inutile.
Et en bonus - Ma checklist express !
Remplacer les partial chauds par partialCached + variantes propres (au minimum : header, footer, nav, sidebar).
getJSON : config de cache (maxAge) et --ignoreCache en dev.
--templateMetrics + --templateMetricsHints pour viser les pires templates.
Pagination : passer à [pagination].pagerSize si vous migrez.
Utiliser site.RegularPages au lieu de .Site.Pages dans les boucles.
Module mounts avec excludeFiles pour éviter que Hugo scanne backups/archives.
Prod : --gc + --cleanDestinationDir + --minify.
Cache permanent pour images/assets (maxAge = -1).
Voilà. Avec tout ça, je suis passé de plusieurs heures à quelques minutes pour ~60 000 pages. Les clés : partialCached partout où la sortie ne bouge pas, un bundle CSS/JS propre avec fingerprint/SRI, et site.RegularPages pour ne pas trimbaler les taxonomies dans les boucles.
N’oubliez pas de lancer hugo --templateMetrics --templateMetricsHints pour trouver ce qui coûte cher lors de la génération. Vous verrez, dans 90% des cas c’est un partial appelé 10 000 fois non mis en cache, ou une boucle sur .Site.Pages trop large. Réparez ça, et votre build respira de nouveau.
Et si après tout ça votre build met encore plus de 10 s pour builder moins de 1000 pages, c’est qu’il y a un loup dans vos templates. Réduisez la surface des boucles, chassez les recalculs, et mettez partialCached partout où c’est pertinent !