%

Hugo : Render Hook Image & Video

Comment utiliser le procédé de 'render hook' pour gérer à la fois l'inclusion d'image ou de vidéo dans Hugo avec seulement la syntaxe image markdown.

Détails relatifs à l'article

Article publié, le
et modifié le
7 minutes de lecture

Cet article contient 1355 mots.

Identifiant de l'article : tag:doc.huc.fr.eu.org,2026-09-22:/fr/web/hugo/render-hook-image-video/#549cbe980aaca38db2145cd8b5d2c58ae295206a2350eb3edc5f63412f7fa22c


Source brute de l'article :
Commit version : 4b003bf


Cet article est aussi disponible sur le protocole Gemini :
gemini://gmi.it-log.fr.eu.org/fr/web/hugo/render-hook-image-video.gmi


Description

Hugo gère la syntaxe Markdown des images, par le biais du moteur Goldmark.

Le format Markdown pour les images est connu :

![Tux](tux.jpg "Logo du Manchot")

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 a et picture
  • 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 w dans 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 :

![Logo au format PNG](/images/Logo_full_192px.png "Mon logo au format PNG")

Logo au format PNG
Mon logo au format PNG

![Logo au format PNG, taille demandée de 128 px](/images/Logo_full_192px.png?w=128 "Mon logo au format PNG, retaillé à 128px")

Logo au format PNG, taille demandée de 128 px
Mon logo au format PNG, retaillé à 128px

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

![Logo au format SVG](/svg/Logo_final.svg "Mon logo au format SVG")

Logo Emblème Stéphane HUC Logo Emblème Stéphane HUC
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)

![Vidéo de "Big Buck Bunny"](https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4 "Big Buck Bunny")

Le navigateur utilisé ne prend pas en charge les vidéos intégrées. Voici le lien pour pouvoir télécharger la vidéo : https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4

TL;DR

Pour les impatients, allez donc télécharger :

Pour les détails, lisez l’article ! ;)

Remerciements


Voilà !


Enjoy-ID!
Enjoy-IT!