Description
Hugo gère la syntaxe Markdown des images, par le biais du moteur Goldmark.
Le format Markdown pour les images est connu :

Hugo permet de surcharger le comportement par le biais de “render hook” - qui se traduirait en français par ‘crochet de rendu’. Ceux-ci sont à écrire généralement dans un sous-répertoire nommé _markup du répertoire principal layouts.
J’utilise historiquement celui créé par “Veriphor LLC”, déposé sous licence Apache 2.0… parce qu’à l’époque en 2023, cela m’était plus simple et je n’avais vraiment pas compris la mécanique derrière. Donc, pourquoi écrire ce que d’autres avaient certainement mieux fait que moi…
Ce render hook nécessite la modification de la configuration des modules à monter, tel que :
mounts:
- source: assets
target: assets
- source: static
target: assets
Puis l’ajout de ces paramètres dans la configuration :
render_hooks:
image:
# ignore (default), warning, or error (fails the build)
errorLevel: warning
(…)
Ce sont les prérequis…
Modifications
Puis au courant de l’année 2026, j’ai commencé à triturer ce modèle pour essayer :
- d’apporter un peu plus de sens, en ajoutant l’élément
aetpicture - de détecter si une taille d’affichage est demandée,
- puis ajouter des sources pour l’affichage des formats webp, puis avif
- de modifier le code pour gèrer les images au format svg
- et aujourd’hui, je me suis posé la question de gérer l’affichage de vidéo.
Nativement, Markdown ne supporte pas l’affichage vidéo.
Je veux pouvoir utiliser la syntaxe pour les images et que le crochet fasse la détection d’une image ou d’une vidéo et affiche le code adéquat.
Après le gros du travail effectué par le code originel, j’ai initialisé quelques variables utiles pour le traitement futur d’un média image ou vidéo, avant l’initialisation du dictionnaire d’attributs $attrs :
(…)
{{- /* Initialize variables. */}}
{{- $alt := .PlainText -}}
{{- $content := "" -}}
{{- $dest := .Destination | safeURL -}}
{{- $ext := $r.MediaType.SubType -}}
{{- $height := "" -}}
{{- $media := $r.MediaType.MainType -}}
{{- $src := $r.RelPermalink -}}
{{- $srcset := dict }}
{{- $title := .Title | transform.HTMLEscape -}}
{{- $type := $r.MediaType -}}
{{- $width := "" -}}
(…)
$r étant la variable ressource…
J’ai de fait modifié l’initialisation du dictionnaire $attrs, tel que :
(…)
{{- /* Initialize attributes. */}}
{{- $attrs := merge .Attributes (dict) }}
{{- if eq $media "image" }}
{{- $img := $r -}}
{{- with $r }}
{{- if eq $ext "svg" }}
(…)
{{- else }}
{{- $attrs = merge $attrs (dict "id" $id "alt" $alt "title" $title "src" $src) }}
(…)
En effet, le besoin de gérer les attributs d’image n’est sensible que pour les images autres que le format SVG, du moins actuellement.
Mediatype
Une des choses que fait très bien Hugo est la détection du type de media. Il est ainsi possible grâce à l’utilisation de l’objet .MediaType d’une ressource de savoir si c’est une image ou autre chose, telle une vidéo.
.MainType est la méthode d’objet qui retourne le type principal de la ressource, initialisée dans la variable $media.
Il suffit ensuite d’utiliser la variable $media pour alimenter du code spécifique :
{{- if eq $media "image" }}
(…)
{{- else if eq $media "video" }}
(…)
{{- end }}
Image
Déterminer la taille d’image
Pour déterminer si une taille d’affichage de l’image est demandée, je rajoute dans la syntaxe d’image l’argument suivant w et précise la taille désirée, tel que ?w=250 par exemple.
Pour gérer cette particularité dans le render hook, j’ai dû le modifier pour déterminer si l’ajout était fait dans l’url de l’image :
{{- /* Determine if 'w=' on $u */ -}}
{{ $w := "" }}
{{- if findRE "w=" $u }}
{{ $w = replaceRE ".*w=([^ ]*).*" "$1" $u }}
{{- end }}
Puis il faut gérer la retouche de la taille d’image, dans le bloc de code du media image :
(…)
{{- $img := $r -}}
{{- with $r }}
(…)
{{- if $w }}
{{- $width = $w -}}
{{- $img = $r.Resize (printf "%sx" $w) -}}
{{- with $img }}{{- $height = .Height -}}{{- end -}}
{{ else }}
{{- $height = .Height -}}
{{- $width = .Width -}}
{{- end }}
{{- $attrs = merge $attrs (dict "height" (string $height) "width" (string $width)) }}
(…)
L’image est retravaillée s’il y a présence de l’argument $w ; le code détecte la largeur de l’image créée, sinon si l’argument n’est pas présent, il récupère la taille de l’image ressource.
Ainsi les paramètres height et width ont toujours une valeur adéquate, à restituer dans le code source HTML lié.
Sources avif, webp
/!\ Depuis la version 0.162.0, Hugo est enfin capable de gèrer le format Avif !
J’ai donc retouché le code…
Pour ajouter les sources webp, et avif, cela se fait en deux temps :
- Une première fois pour retoucher l’image aux formats avif et webp :
(…)
{{/* Hugo.version need to: 0.162.0, to support Avif */}}
{{- if site.Params.enable.img.avif }}
{{- with .Resize (printf "%dx%d avif" ($height | int) ($width | int)) }}
{{ $srcset = merge $srcset (dict "avif" (dict "srcset" .RelPermalink "type" .MediaType)) }}
{{- end }}
{{- end }}
{{- if site.Params.enable.img.webp }}
{{- with .Resize (printf "%dx%d webp" ($height | int) ($width | int)) }}
{{ $srcset = merge $srcset (dict "webp" (dict "srcset" .RelPermalink "type" .MediaType)) }}
{{- end }}
{{- end }}
(…)
PS : Comme vous pouvez le remarquer, j’ai fait le choix d’appeler des booléens depuis la configuration des paramètres, pour activer ou non le support desdits formats.
- Une deuxième fois, dans le code HTML restitué :
(…)
<picture>
{{- with $srcset }}
{{ range $srcset }}
{{ with . }}
<source srcset="{{ .srcset }}" type="{{ .type }}">
{{- end }}
{{- end }}
{{- end }}
<img (…) >
</picture>
(…)
Déterminer le format svg
Pour déterminer le format svg, l’utilisation de la méthode .SubType de l’objet ressource est viable.
Au sein du code déterminant le média image, la condition si le sous-type du média est svg alors il récupère le contenu du svg, puisque SVG est un contenu textuel.
(…)
{{- if eq $ext "svg" }}
{{- $content = .Content | safeHTML -}}
{{- else }}
(…) ; traitement des autres formats d'image
{{- end }}
(…)
Puis dans un deuxième temps, l’affichage du code HTML, tel que :
(…)
<figure role="figure" {{- with $title }}aria-label="{{ . }}"{{- end }}>
<a href="{{ $src }}" {{- with $title }}title="{{ $title }}"{{- end }}>
{{- with $content }}
{{ . }}
{{- else }}
(…)
- je réfléchis à l’ajout du paramètre
wdans l’url de l’image svg - mais pour l’instant rien ne me satisfait…
Vidéo
Aucun traitement d’image n’est ni fait ni à faire - sur la vidéo… le code appel directement le HTML correspondant nécessaire :
(…)
{{- else if eq $media "video" }}
<video controls height="" loading="lazy" preload="metadata" width="50%">
<source src="{{ $src }}" type="{{ $type }}">
<p>
<em>{{ T "txtNoSupportVideo" }}</em>
<a href="{{ $dest }}" title="{{ $title }}">{{ $dest }}</a>
</p>
</video>
(…)
- faites attention au texte linguistique…
Utilisation
Comment je l’utilise ?
Très simplement :
⇒ une image, tel mon logo, au format PNG :


⇒ une image, tel mon logo, au format SVG :

⇒ une vidéo, au hasard celle de “Big Buck Bunny” (sous Licence CC 3.0, par la fondation Blender, hébergé sur archive.org)

TL;DR
Pour les impatients, allez donc télécharger :
- le code actuel du render hook image
Pour les détails, lisez l’article ! ;)
Remerciements
- l’article Link and image render hooks
Voilà !