Resize or convert images to WebP conditionally

I’m trying converting all images to webp format and resizing them to a maximum width of 600px. I would like to keep the original size if the image is already less than 600px wide. Markdown posts still use original images (bundle resources).

Can anyone share a code snippet with me so it can do it automatically, such as an img tag with a webp file and lazy load?

Which images?

  • Page resources?
  • Global resources?
  • Remote resources?
  • Images in the static directory?

Thanks, it would be images in bundle content posts.

Assuming you add those images to a page using Markdown image links, you’ll need to implement an image render hook based on Hugo’s embedded image render hook.

Here’s a variation of the image render template I’ve been using, with some adjustments.

  • If you’re not using the extended version of Hugo, then images will be resized but not converted to WEBP.
  • Although it is possible with Hugo, remote resources won’t be resized or converted by the following code. This can be resource heavy and time consuming if not used with caution.
  • GIF images won’t be converted as the library Hugo uses cannot yet handle animated GIFs. Remove the respective condition if that’s not an issue.
  • Local SVG images will be embedded within the HTML output, which is opinionated but in my use case permits coloring them with CSS. We’re assuming they contain safe HTML.
<!-- layouts/partials/render-image.html -->
<!-- get image URL from Markdown tag -->
{{- $src := (.Destination | safeURL) -}}
{{ $src = replace $src "./" "" }}
<!-- check .Store.Get "imgwidth" or use default width -->
{{- $imgwidth := .Page.Store.Get "imgwidth" | default 600 -}}
<!-- check if it exists as a page resource -->
{{- with (or ((.Page.Resources.ByType "image").Get $src) ((resources.ByType "image").Get $src)) -}}
	<!-- if SVG then output its contents -->
	{{- if strings.HasSuffix $src ".svg" -}}
		{{ .Content | safeHTML }}
	{{- else -}}
		{{ $resized := . }}
		<!-- resize if wider than "imgwidth" but if GIF file then avoid resizing animations; WebP animations not yet supported; see upstream ticket: https://github.com/golang/go/issues/53364 -->
		{{ if and (gt .Width $imgwidth) (ne .MediaType.SubType "gif") }}
			{{ if hugo.IsExtended }}
				{{- $resized = .Resize (print $imgwidth "x webp") -}}
			{{ else }}
				{{- $resized = .Resize (print $imgwidth "x") -}}
				{{ warnf "Missing Hugo Extended, in partial render-image.html" }}
			{{ end }}
		{{ end }}
		<img src="{{ $resized.RelPermalink }}"
		width="{{ $resized.Width }}"
		height="{{ $resized.Height }}"
		{{ with $.PlainText }} alt="{{ . }}" {{ end }}
		{{ with $.Title }} title="{{ . | plainify | htmlEscape }}" {{ end }}
		{{- range $k, $v := $.Attributes -}}
			{{- if $v -}}
				{{- printf " %s=%q" $k ($v | htmlEscape) | safeHTMLAttr -}}
			{{- end -}}
		{{- end -}}
		loading="lazy" decoding="async" />
		<!-- closing tag on img intented, for compatibility with XML output -->
	{{- end -}}
{{- else -}}
	<!-- or otherwise its an external resource so load the URL -->
	<img src="{{ .Destination | safeURL }}"
	{{ with $.PlainText }} alt="{{ . }}" {{ end }}
	{{ with $.Title }} title="{{ . | plainify | htmlEscape }}" {{ end }}
	{{- range $k, $v := .Attributes -}}
		{{- if $v -}}
			{{- printf " %s=%q" $k ($v | htmlEscape) | safeHTMLAttr -}}
		{{- end -}}
	{{- end -}}
	loading="lazy" decoding="async" data-src="external" />
{{- end -}}

I hope that helps. Notes to improve any of the above, welcome!