HugoでWebPとAVIF形式の画像ファイルを扱う

WebP や AVIF 形式の画像ファイルは、より高品質でありながらファイルサイズが小さいため、近年注目を集めています。しかし、これらの画像形式に対応していないブラウザが存在しているため、後方互換性に注意を払い実装する必要があります。

本記事では、Hugo で WebP や AVIF 形式の画像ファイルを扱う方法を紹介します。

AVIF/WebP の HTML マークアップについて

まず基本となる AVIF/WebP のマークアップは下記の通りです。

<picture>
  <source srcset="hogehoge.avif" type="image/avif" />
  <source srcset="hogehoge.webp" type="image/webp" />
  <img
    src="hogehoge.jpg"
    alt="hogehoge is cool"
    loading="lazy"
    decoding="async"
    width="400"
    height="300"
  />
</picture>

WebP/AVIF に対応しているブラウザであれば source タグに指定したファイルを描画し、そうでない場合は img タグで指定した画像が表示されます。

単純な HTML ファイルであればこの通りマークアップすることで事足りますが、Hugo では markdown 記法で挿入した画像を変換する必要があります。

Hugo のデフォルトテンプレートをオーバーライドする

Hugo の Markdown Render Hooks と呼ばれる機能を使うことで、markdown のレンダリングを行うテンプレートをカスタマイズすることができます。

参考: Markdown Render Hooks

画像をレンダリングするテンプレートを上書くには、下記ディレクトリ構造にrender-image.htmlという名前でファイルを用意します。

layouts/
└── _default/
    └── _markup/
        └── render-image.html

render-image.html のコード

私は下記のように render-image.html を実装しています。

画像のマークアップ部分は様々な箇所で使い回せるため、partials に置いて共通化しました。

render-image.html

{{ partial "atoms/picture.html" (dict "image" (.Destination | safeURL) "alt" .Text) }}

atoms/picture.html

<picture>
  {{ $isAVIF := eq (path.Ext .image) ".avif" }}
  {{ $isWebP := eq (path.Ext .image) ".webp" }}
  {{ $imgSrc := "" }}

  {{ if or $isAVIF $isWebP }}
    {{ $type := "" }}
    {{ if $isAVIF }}
      {{ $type = "avif" }}
    {{ else if $isWebP }}
      {{ $type = "webp" }}
    {{ end }}
    <source srcset="{{ .image }}" type="image/{{ $type }}" />

    {{ $jpgPath := replace .image (printf ".%s" $type) ".jpg" }}
    {{ $jpgPathStatic := printf "static/%s" $jpgPath }}
    {{ if (fileExists $jpgPathStatic) }}
      {{ $imgSrc = $jpgPath }}
    {{ end }}

    {{ $pngPath := replace .image (printf ".%s" $type) ".png" }}
    {{ $pngPathStatic := printf "static/%s" $pngPath }}
    {{ if (fileExists $pngPathStatic) }}
      {{ $imgSrc = $pngPath }}
    {{ end }}
  {{ else }}
    {{ $imgSrc = .image }}
  {{ end }}
  <img
    src="{{ $imgSrc }}"
    {{ with .alt }}alt="{{ . }}"{{ end }}
    loading="lazy"
    decoding="async"
    {{ with imageConfig (printf "/static%s" $imgSrc) }}
      width="{{ .Width }}" height="{{ .Height }}"
    {{ end }}
  />
</picture>

まとめ

実は AVIF/WebP が Microsoft edge で未対応だったことを知らず、調査が不十分な状態で AVIF に置き換えていたのですが、あとになって edge で画像が表示できていないことに気づき修正しました。edge も早く対応してくれると嬉しいですね。

ともあれ、これで AVIF/WebP を使うことでサイトの読み込み速度を向上させ、ユーザーエクスペリエンスを向上させることができます。やったぜ!