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.
Prérequis
Module
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 :
modules:
mounts:
- source: assets
target: assets
- source: static
target: assets
Paramètres
Puis l’ajout de ces paramètres dans la configuration :
params:
(…)
render_hooks:
DontDlMedia:
audio: true
image: false
video: true
image:
# ignore (default), warning, or error (fails the build)
errorLevel: warning
(…)
- ajout des paramètres
DonotDlMedia: les valeurs boolènnes correspondantes aux medias permettent ou non le téléchargement du média, lors de la publication. Si la valeur boolènne est vraie, alors la méthode utilisée seraheadau lieu deget. Ces paramètres n’interagissent que dans le contexte des ressources à distance, et non locales.
Les medias en question peuvent avoir des tailles considérables ; dans le contexte de ressources partagées, cela peut avoir un impact certain (- exemple d’un service git-pages mutualisé, …).
À ce propos, j’ai aussi ajouté principalement une nouvelle option de paramètres :
params:
(…)
render:
video:
size: "300x"
- ce paramètre est utile pour fixer la taille du lecteur vidéo - pour rappel, la norme relative définit que la taille doit être en pixels et non pas pourcentage… Il vous faut les ajouter !
Sécurité
Deux aspects, liés à la sécurité des données gérées par Hugo, sont à mentionner…
⇒ Il est nécessaire d’autoriser la réception de données par les méthodes GET et HEAD.
Pour cela modifier la configuration security, pour ajouter/modifier les méthodes http, tel que :
security:
http:
(…)
methods:
- (?i)GET|HEAD
⇒ Il peut-être utile de gérer les mediatypes autorisés, toujours dans security, surtout si vous avez modifié la politique par défaut null.
Par exemple, en ajoutant les mediatypes audio et/ou vidéo concernés, tel que :
security:
http:
mediaTypes:
- "^audio/mpeg$"
- "^video/mp4$"
Très utile, quand Hugo refuse d’obtenir le contenu et que la console restitue le message sybillin suivant :
WARN template: _markup/render-image.html:136:27: executing "_markup/render-image.html" at <resources.GetRemote>: error calling GetRemote: failed to resolve media type for remote resource "https://dn710802.ca.archive.org/0/items/tour_du_monde_en_80_jours_librivox/tour_monde_01_verne_64kb.mp3". See web/hugo/render-hook-image-video.md
Dans les faits, Hugo n’affichera pas le contenu distant… à retenir !
Modifications
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
- en date du 2 Oct., je me suis posé la question de gérer l’affichage de vidéo.
- en date du 5 Oct, j’ai ajouté la gestion de l’affichage d’un lecteur audio.
Nativement, Markdown ne supporte ni l’affichage audio, ni vidéo.
Je veux pouvoir utiliser la syntaxe pour les images et que le crochet fasse la détection d’une image, d’une audio 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 := .MediaType.SubType -}}
{{- $height := "" -}}
{{- $media := .MediaType.MainType -}}
{{- $size := len .Content -}}
{{- $src := .RelPermalink -}}
{{- $srcset := dict }}
{{- $title := $.Title | safeHTML -}}
{{- $type := .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 "audio" }}
(…)
{{- 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…
Audio
Aucun traitement d’image n’est ni fait ni à faire - sur de l’audio…
Le code appel directement le HTML nécessaire :
(…)
{{- else if eq $media "audio" }}
<figure role="figure" {{- with $title }}aria-label="{{ . }}"{{- end }}>
<audio id="{{ $id }}" controls controlslist="nodownload" loading="lazy" preload="metadata" src="{{ $dest }}" >
<p>
<a href="{{ $src }}" {{- with $title }}title="{{ . }}"{{- end }}>
{{ T "txtDlAudio" }}
</a>
</p>
</audio>
{{- with $title }}<figcaption>{{ . }}</figcaption>{{- end }}
</figure>
(…)
- faites attention au texte linguistique…
Vidéo
Là encore, aucun traitement d’image n’est ni fait ni à faire - sur la vidéo…
Le code appel directement le HTML nécessaire :
(…)
{{- else if eq $media "video" }}
<video id="{{ $id }}" controls controlslist="nodownload" height="" loading="lazy" playsinline preload="metadata" src="{{ $dest }}" width="300px">
<source src="{{ $dest }}" type="{{ $type }}">
<p>
<em>{{ T "txtNoSupportVideo" }}</em><br>
<a href="{{ $dest }}" title="{{ $title }}">{{ $dest }}</a>
</p>
</video>
(…)
- faites attention au texte linguistique…
Utilisation
Comment je l’utilise ?
Très simplement :
Image
⇒ une image, hébergée localement, tel mon logo, au format PNG :


⇒ une image, hébergée localement, tel mon logo, au format SVG :

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

⇒ ou, tel cette image :
⇒ ou la même, mais avec une taille demandé de 64 px
Audio
⇒ un fichier audio, par exemple un livre nommé “Le tour du monde en quatre-vingts jours” (Collection LibriVox, hébergé sur archive.org, domaine public)

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

⇒ La même enregistrée localement :

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à !