Cuando estaba migrando mi sitio de WordPress a GoHugo quería aprovechar todo el trabajo que implicaba la migración para asegurarme también de optimizar las imágenes.

Lo que necesitaba era:

  • Reducir el tamaño de la imagen original conservando la calidad
  • Cargar las imágenes de manera perezosa con el atributo loading en lazy de HTML
  • Ofrecer imágenes en distintos tamaños para cada pantalla. Es decir, ofrecer una imagen más pequeña para móviles y una más grande para dispositivos de escritorio
  • Usar picture y source

De este modo mi blog iba a cargar más rápido. Al final terminé creando un shortcode que me permite incrustar imágenes cumpliendo con todas las características que acabo de mencionar.

No creo que sea el primero al que se le haya ocurrido esto, pero igualmente te vengo a compartir cómo solucioné este problema a través de un shortcode propio de Hugo.

Creando shortcode de GoHugo para imágenes

Lo único que tenemos que hacer es leer la imagen que se quiere insertar, redimensionarla a varios tamaños y crear el picture con los source necesarios.

Decidí ofrecer mis imágenes en webp y jpg. Pero bueno, comencemos con lo primero. Para empezar me he asegurado de que el procesamiento solo se haga en producción, así al momento de estar previsualizando no tengo que esperar mucho tiempo, ya que recuerda que el procesamiento de imágenes es muy pesado.

{{ $src := .Get "src" }}
{{ $alt := .Get "alt" | default "" }}
{{ $loading := .Get "loading" | default "lazy" }}
{{ $imagenOriginal := .Page.Resources.GetMatch $src }}
{{ if not $imagenOriginal }}
{{ errorf "optimized_image: recurso %q no encontrado en el archivo: %s" $src .Page.Path }}
{{ end }}
{{if not hugo.IsProduction}}
<img src="{{$imagenOriginal.RelPermalink}}" alt="{{$alt}}">
<strong>Esto no debería aparecer en producción. Si lo hace, revisa optimized_image</strong>

En caso de que estemos en producción entonces comienzo definiendo un arreglo de medidas. Estas medidas se me han ocurrido a mí y puede que en tu caso quieras modificarlas dependiendo de los tamaños que quieras tener.

También defino una cadena que va a tener el atributo que colocaré al srcset del source:

{{ $medidas := slice 320 600 800 1000}}
{{ $cadenaSrcSet := "" }}
{{ $cadenaSrcSetJpeg := "" }}

Recorro mis medidas y dentro de dicho recorrido:

  • Reviso que la imagen mida más que la medida actual para redimensionarla, ya que por ejemplo, si tuviera una imagen de 600 de ancho no vale la pena redimensionarla a 800 ni a 1000
  • Redimensiono la imagen al ancho actual según el slice. Es decir, en el primer paso la redimensionaré a 320
  • Aquí aprovecho para reducir la calidad (q75) y convertir a webp. La redimensión se hace con el algoritmo Lanczos
  • Hago lo mismo para la imagen JPG
  • Concateno el src a $cadenaSrcSet y $cadenaSrcSetJpeg ya que recuerda que el srcset es algo como https://example.com/url_imagen.webp 320w

Todo eso está en el siguiente código


{{if lt $medida $imagenOriginal.Width}}
{{ $imagenWebp := $imagenOriginal.Resize (printf "%dx q75 webp Lanczos" $medida) }}
{{ $imagenJpeg := $imagenOriginal.Resize (printf "%dx q75 jpeg Lanczos" $medida) }}
{{$cadenaSrcSet = printf "%s %s %dw," $cadenaSrcSet $imagenWebp.RelPermalink $imagenWebp.Width}}
{{$cadenaSrcSetJpeg = printf "%s %s %dw," $cadenaSrcSetJpeg $imagenJpeg.RelPermalink $imagenJpeg.Width}}

Y finalmente, ya fuera del recorrido del slice, añado la imagen en su tamaño original:

{{$imagenOriginalWebp := $imagenOriginal.Resize (printf "%dx q75 webp Lanczos" $imagenOriginal.Width)}}
{{$imagenOriginalJpeg := $imagenOriginal.Resize (printf "%dx q75 jpeg Lanczos" $imagenOriginal.Width)}}
{{$cadenaSrcSet = printf "%s %s %dw" $cadenaSrcSet $imagenOriginalWebp.RelPermalink $imagenOriginalWebp.Width}}
{{$cadenaSrcSetJpeg = printf "%s %s %dw" $cadenaSrcSetJpeg $imagenOriginalJpeg.RelPermalink $imagenOriginalJpeg.Width}}

Las últimas líneas son para definir el atributo sizes que se calcula de acuerdo a tu CSS (hablaré de eso más adelante). El resto de código queda así:

{{$sizes := "(max-width: 768px) 91.25vw, 720px"}}
<picture>
    <source type="image/webp" srcset="{{$cadenaSrcSet}}" sizes="{{$sizes}}">
    <source type="image/jpeg" srcset="{{$cadenaSrcSetJpeg}}" sizes="{{$sizes}}">
    <img src="{{$imagenOriginalJpeg.RelPermalink}}" alt="{{$alt}}" width="{{$imagenOriginalJpeg.Width}}"
        height="{{$imagenOriginalJpeg.Height}}" loading="{{$loading}}" />
</picture>
{{end}}

Y el código completo del shortcode queda así:


{{ $src := .Get "src" }}
{{ $alt := .Get "alt" | default "" }}
{{ $loading := .Get "loading" | default "lazy" }}
{{ $imagenOriginal := .Page.Resources.GetMatch $src }}
{{ if not $imagenOriginal }}
{{ errorf "optimized_image: recurso %q no encontrado en el archivo: %s" $src .Page.Path }}
{{ end }}
{{if not hugo.IsProduction}}
<img src="{{$imagenOriginal.RelPermalink}}" alt="{{$alt}}">
<strong>Esto no debería aparecer en producción. Si lo hace, revisa optimized_image</strong>
{{else}}
{{ $medidas := slice 320 600 800 1000}}
{{ $cadenaSrcSet := "" }}
{{ $cadenaSrcSetJpeg := "" }}
{{ range $medida := $medidas }}
{{if lt $medida $imagenOriginal.Width}}
{{ $imagenWebp := $imagenOriginal.Resize (printf "%dx q75 webp Lanczos" $medida) }}
{{ $imagenJpeg := $imagenOriginal.Resize (printf "%dx q75 jpeg Lanczos" $medida) }}
{{$cadenaSrcSet = printf "%s %s %dw," $cadenaSrcSet $imagenWebp.RelPermalink $imagenWebp.Width}}
{{$cadenaSrcSetJpeg = printf "%s %s %dw," $cadenaSrcSetJpeg $imagenJpeg.RelPermalink $imagenJpeg.Width}}
{{end}}
{{ end }}
{{$imagenOriginalWebp := $imagenOriginal.Resize (printf "%dx q75 webp Lanczos" $imagenOriginal.Width)}}
{{$imagenOriginalJpeg := $imagenOriginal.Resize (printf "%dx q75 jpeg Lanczos" $imagenOriginal.Width)}}
{{$cadenaSrcSet = printf "%s %s %dw" $cadenaSrcSet $imagenOriginalWebp.RelPermalink $imagenOriginalWebp.Width}}
{{$cadenaSrcSetJpeg = printf "%s %s %dw" $cadenaSrcSetJpeg $imagenOriginalJpeg.RelPermalink $imagenOriginalJpeg.Width}}
{{$sizes := "(max-width: 768px) 91.25vw, 720px"}}
<picture>
    <source type="image/webp" srcset="{{$cadenaSrcSet}}" sizes="{{$sizes}}">
    <source type="image/jpeg" srcset="{{$cadenaSrcSetJpeg}}" sizes="{{$sizes}}">
    <img src="{{$imagenOriginalJpeg.RelPermalink}}" alt="{{$alt}}" width="{{$imagenOriginalJpeg.Width}}"
        height="{{$imagenOriginalJpeg.Height}}" loading="{{$loading}}" />
</picture>
{{end}}

Por cierto, además del source para webp y jpeg también he incluido una imagen fallback en caso de que el navegador no soporte source

Lazy loading

Fíjate en que en el ejemplo anterior basta con poner el atributo loading en lazy y el navegador se encargará del resto

Modo de uso

Ya está liste para que lo uses, solo no te olvides de configurar el atributo sizes con la variable $sizes.

En mi caso lo he colocado en layouts/_shortcodes/optimized_image.html y lo uso así:

{{<optimized_image src="ubicación_imagen_relativa_al_markdown.extensión" alt="Aquí el alt">}}

Mi contenido está dividido en carpetas y mi Markdown se llama index.md. Basta con colocar las imágenes en la misma carpeta para que sean hermanas de index.md y puedo incluirlas simplemente escribiendo su nombre completo.

Cuando uses ese shortcode el HTML resultante será algo como:

<picture>
    <source type=image/webp
        srcset="/blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_bf82a67e70042c44.webp 320w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_e40f06e6e074e9c0.webp 600w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_3d2e92e90c43105.webp 800w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_fba692a1cadb9744.webp 922w"
        sizes="(max-width: 768px) 91.25vw, 720px">
    <source type=image/jpeg
        srcset="/blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_7065c85d7c251805.jpg 320w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_c6b08d0dc77de7ef.jpg 600w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_cd434a2830d05217.jpg 800w, /blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_8afc0da0cfaf148f.jpg 922w"
        sizes="(max-width: 768px) 91.25vw, 720px">
    <img src=/blog/posts/migrando-wordpress-hugo-ubuntu-rocky/%C3%9Altima%20captura%20de%20pantalla%20antiguo%20servidor%20Ubuntu_hu_8afc0da0cfaf148f.jpg
        alt="Captura que muestra el inicio de sesión exitoso a un servidor Ubuntu" width=922 height=696 loading=lazy>
</picture>

Sobre el atributo sizes

Este atributo debe ser calculado de acuerdo al tamaño porcentual del ancho total que ocupan las imágenes en cada viewport. Puedes usar vw y px.

Te dejo un video de YouTube para que complementes esta información y entiendas el atributo:

Si el post ha sido de tu agrado te invito a que me sigas para saber cuando haya escrito un nuevo post, haya actualizado algún sistema o publicado un nuevo software. Facebook | X | Instagram | Telegram | También estoy a tus órdenes para cualquier contratación en mi página de contacto