Image Partial for Asset Processing

I recently spoke at hugoconf2022 and I am trying to make my template more useful.

The repository is GitHub - reillymedia/hugoconf-perfect-lighthouse-scores: Perfect Lighthouse Scores 2022

It is open source and free for anyone to download.

I am trying to pass an image url from a template layout into a partial and have hugo images process the required images src set images. The only way i have been able to make this work so far is to have the actual image in the img/img.html (image processing partial) like this. This however only processes one image. I would like to be able to pass the variables using dict and produce the srcset images for each image.

Below is what I currently have.

{{/* get file that matches the filename as specified as src=“” in shortcode */}}
{{ $src := resources.Get “images/banner.webp” }}

{{/* set image sizes, these are hardcoded for now, x dictates that images are resized to this width */}}

{{ $tinyw := default “500x webp” }}
{{ $smallw := default “883x webp” }}
{{ $mediumw := default “1200x webp” }}
{{ $largew := default “1500x webp” }}

{{/* resize the src image to the given sizes */}}

{{ .Scratch.Set “tiny” ($src.Resize $tinyw) }}
{{ .Scratch.Set “small” ($src.Resize $smallw) }}
{{ .Scratch.Set “medium” ($src.Resize $mediumw) }}
{{ .Scratch.Set “large” ($src.Resize $largew) }}

{{/* add the processed images to the scratch */}}

{{ $tiny := .Scratch.Get “tiny” }}
{{ $small := .Scratch.Get “small” }}
{{ $medium := .Scratch.Get “medium” }}
{{ $large := .Scratch.Get “large” }}

{{/* only use images smaller than or equal to the src (original) image size, as Hugo will upscale small images /}}
{{/
set the sizes attribute to (min-width: 35em) 1200px, 100vw unless overridden in shortcode */}}

Test

The code is

{{ partial "img/img.html" . }}

Perfect Lighthouse Scores with Hugo

The world's fastest static site generator.

Fix Your Lighthouse Scores

I want to pass img partial from the coverbanner.html using dict and have it work so that all the srcsets will work and it will be easy to pass something like {{ partial “img/img.html” dict ( “url” “images/banner.jpg” “alt” “Perfect Scores” }}

!–Cover Banner–>

{{ partial "img/img.html" . }}

Perfect Lighthouse Scores with Hugo

The world's fastest static site generator.

Fix Your Lighthouse Scores

The code in the repository works as is in the repository but I am not able to use dict for each image passed in every partial. Any help would be appreciated.

Here is a good example of how to pass variables to partials:

And thanks for your interesting talk at HugoConf!

Sorry for the confusion. I am not looking for a shortcode. I am looking to have hugo images process assets in a layout using a partial. I know it looks like that I am trying to achieve that through a partial but i am using Laura Kalbag’s code and have not cleaned it up yet.

First, thanks for your presentation at HugoConf!

You might get some value from a previous answer I gave here to a somewhat similar question:

1 Like

I have a repo (more complicated that what you want, but maybe you can learn what you need from it) that implements both a image render-hook, a shortcode, and can also be called as a partial directly from a layout.

This allows you to write Markdown and get responsive images (which I believe is what you are after).

The repo is here

and I have a site that shows the various options in use (although it’s not made to be ‘pretty’, but to show the mechanics): https://image-handling-mod.wildtechgarden.ca/

I hope that can help you.

4 Likes

that is one hell of a repository of robust functions. wow. serious image processing powers. In your page, i saw an image showing what looked like a debug display of all page variables, where did you get that/do that?

1 Like

Glad you like the repo! For the debug display I find it more useful as a learning tool than actual debugging, but it’s from my repo here:

With a demo site at https://hugo-test-debug-tables.wildtechgarden.ca/

Maybe @davidsneighbour can comment on the status of his debug module that emits output in the build logs and looks like it will be (is?) more useful for actual debugging.

2 Likes

“Displaying of all page variables” is possible today. I am currently working on themeability and something I like to call “debugging formatter” where you can define how the output is structured and looks like per variable/object type.

But showing all page variables is as easy as {{ partial "debugprint.html" $page }}. A test partial {{ partial "debugpage.html $page }} is available too.

If I receive feature requests I will look to build them in.

2 Likes

Thanks for the response. I will look into your repository I appreciate your response.

1 Like

I will look into debugging my issue. I am only just learning and trying to still figure things out thank you for your patience.

I missed this part when I first read your topic; can you be a little more clear on

  1. What exactly you are expecting
  2. What you have tried
  3. The results (error messages and/or rendered results)

EDIT: Also, I’ve managed to get https://wildtechgarden.ca (based on my Dananke Hugo theme to 100% Lighthouse scores for every page I have tested, but what I really want is to be able to test every page via a manually triggered GitHub Action on a site that is not yet deployed – do you have info doing that kind of thing)?

1 Like

To manually run lighthouse you can follow the instructions here. I am sure there is a way to write some code to test every page via a manually triggered github action.

Follow the instructions here.

To get it to run I used the following code under a file I named local.js to give warm and cold loads of the lighthouse scores.

import fs from ‘fs’;
import open from ‘open’;
import puppeteer from ‘puppeteer’;
import {startFlow} from
‘lighthouse/lighthouse-core/fraggle-rock/api.js’;

async function captureReport() {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();

const flow = await startFlow(page, {name: ‘Single Navigation’});
await flow.navigate(‘http://localhost:1313/’);

// Warm navigation report
{
const targetPage = page;
await flow.navigate(‘http://localhost:1313/’, {
stepName: ‘Warm navigation’,
configContext: {
settingsOverrides: { disableStorageReset: true },
}
});
}

await browser.close();

const report = await flow.generateReport();
fs.writeFileSync(‘flow.report.html’, report);
open(‘flow.report.html’, {wait: false});
}

captureReport();

To run it type in node local.js in the folder where you installed puppetteer.

For it to work you need to have your local server running your website on hugo then run the node local.js command in the puppetter folder.

I believe netlify has a plugin similar to what you are looking for. GitHub - netlify/netlify-plugin-lighthouse: Netlify Plugin to run Lighthouse on each build it is probably the most appropriate solution because you can set it so the build fails if your pages don’t test within the score threshold that you put in.

As far as my question I am still working on figuring out what needs to be done.

What I am trying to build is something similar to what wordpress has where each image in the asset folder is automatically resized into mutliple sizes and the srcset with new image files are inserted via partial. I have been able to get the shortcode that Laura Kalbag posted to work in markdown files Laura Kalbag – Processing Responsive Images with Hugo. But I am having difficulty making it into a partial where i can post it into template layout files. As far as i am aware shortcodes do not work in layout files.

1 Like

The trick is to create a partial that does what you want and call the partial from the render-hook or shortcode.

For my repo, above, this is what the render-hook looks like:

{{- $markdownImageLinkClass := .Page.Params.imageMarkdownLinkClass | default .Page.Site.Params.imageMarkdownLinkClass -}}
{{- $markdownImageClass := .Page.Params.imageMarkdownClass | default .Page.Site.Params.imageMarkdownClass -}}
{{- $markdownImageWrapper := .Page.Params.imageMarkdownAddWrapper | default .Page.Site.Params.imageMarkdownAddWrapper -}}
{{- $image := partial "helpers/lib/image-handling/find-image-src" (dict "src" .Destination "page" .Page "getRelative" true "ignoreBundleAssets" false) -}}
{{- partial "helpers/wrapped-image" (dict "alt" .Text "altRendered" true "title" .Title "image" $image "page" .Page "noImageWrapper" (not $markdownImageWrapper) "linkClass" $markdownImageLinkClass "class" $markdownImageClass) -}}
{{- /* Remove trailing newlines */ -}}

and the first three lines is to allow page frontmatter or site-wide overrides of defaults in the partial.

Likewise my figure shortcode (to replace Hugo’s built in figure shortcode) is:

{{- $inPage := .Page -}}
{{- partial "helpers/wrapped-image" (dict "width" (.Get "width") "height" (.Get "height") "alt" (.Get "alt") "title" (.Get "title") "image" (partial "helpers/lib/image-handling/find-image-src" (dict "src" (.Get "src") "page" $inPage "getRelative" (.Get "getRelative" | default true) "ignoreBundleAssets" (.Get "ignoreBundleAssets" | default false) "ignoreSiteAssets" (.Get "ignoreSiteAssets" | default false) "ignoreStaticImages" (.Get "ignoreStaticImages" | default true))) "page" $inPage "link" (.Get "link") "target" (.Get "target") "rel" (.Get "rel") "imageWrapper" (default "figure" (.Get "imageWrapper")) "caption" (.Get "caption") "attr" (.Get "attr") "attrLink" (.Get "attrLink") "class" (.Get "class") "singleSize" (.Get "singleSize") "thumbnails" (.Get "thumbnails") "imageClass" (.Get "imageClass") "noVisibleCaption" (.Get "noVisibleCaption") "imageConvertMethod" (.Get "imageConvertMethod") "sizesAttr" (.Get "sizesAttr")) -}}

In the partial there a few things to keep in mind:

Because you may be coming from a shortcode or render-hook context you can’t use site or resources. You will need to use something like:

$curPage.Site.Params or $curPage.Site.resources for what you normally access via site.Params or $.site.Params. (I think newer versions of Hugo may have changed thtat, but my repo supports 0.80.0 up to current, so I haven’t tried.

Also, remember your context will be the dict you pass in and not the usual conext for a partial. You may want to wrap a lot of sections in:

{{ with $curPage }}

so you can (mostly) forget about that.

I recommend reading

if you have not already.

HTH

1 Like

Curious to if the Netlify plugin solved your issue?

I’m not currently using Netlfiy, and do not plan on doing so, for reasons that are my own and not likely to interest others, so a Netlify plugin doesn’t help. The info about the API does, but I’ll need to take some time to build an GitHub Action to take advantage of it.

1 Like

You can download the plugin locally. It just an open source plugin that netlify has listed. It will work without netlify.

I had some more time to go over this issue. I have been able to pass the image through using dict. in my layout file. coverbanner.html

    {{ partial "img/img.html" (dict  "url" "images/banner.jpg" "alt" "Perfect Google Lighthouse Scores" "loading" "eager") }}

When goes it the partial it only processes the src url and does not process the image resizing as specified below.

{{ $src := resources.Get .url }}

{{/* set image sizes, these are hardcoded for now, x dictates that images are resized to this width */}}

{{ $tinyw := default “500x webp” }}
{{ $smallw := default “883x webp” }}
{{ $mediumw := default “1200x webp” }}
{{ $largew := default “1500x webp” }}

{{/* resize the src image to the given sizes */}}

{{ .Scratch.Set “tiny” ($src.Resize $tinyw) }}
{{ .Scratch.Set “small” ($src.Resize $smallw) }}
{{ .Scratch.Set “medium” ($src.Resize $mediumw) }}
{{ .Scratch.Set “large” ($src.Resize $largew) }}

{{/* add the processed images to the scratch */}}

{{ $tiny := .Scratch.Get “tiny” }}
{{ $small := .Scratch.Get “small” }}
{{ $medium := .Scratch.Get “medium” }}
{{ $large := .Scratch.Get “large” }}

{{/* only use images smaller than or equal to the src (original) image size, as Hugo will upscale small images /}}
{{/
set the sizes attribute to (min-width: 35em) 1200px, 100vw unless overridden in shortcode */}}

Perfect Google Lighthouse Scores

This will print out

img sizes=“(min-width: 35em) 883px, 100vw” srcset=“” src=“/images/banner.jpg” alt=“Perfect Google Lighthouse Scores” fetchpriority=“high” loading=“eager” width=“883px” height=“653px”

The issue i am having it is that it is not processing the images for the srcset=“”

The final output should look like what I have on the website:

img sizes=“(min-width: 35em) 883px, 100vw” srcset=“/images/banner_hue2004976bbd8a321a3a91ec6988c0bd7_17952_500x0_resize_q75_h2_box_2.webp 500w, /images/banner_hue2004976bbd8a321a3a91ec6988c0bd7_17952_883x0_resize_q75_h2_box_2.webp 883w” src=“/images/banner.webp” alt=“Perfect Google Lighthouse Scores” fetchpriority=“high” loading=“eager” width=“883px” height=“653px”

Can you shed insight on what I am missing.

Are you using the ‘extended’ version of Hugo, or the ‘basic’? ‘extended’ is required for webp processing.

If that is not an issue, I will take another look. Also you don’t need to use .Scratch with modern Hugo, you can just do:

$tiny := $src.Resize $tinyw

and if you need to assign a new value to $tiny

$tiny = $tiny.RelPermalink

Note the initial set is := and changing the value is just =

of course that assumes once you have the permalink you don’t need to do further operations on the resized resource, but it gives you the idea.

2 Likes

I am using hugo v0.100.2+extended

OMG Thank you so much your suggestions worked.

The code looked like from the cover banner partial:

{{ partial “img/img.html” (dict “url” “images/banner.webp” “alt” “Perfect Lighthouse Scores” “loading” “eager” “width” “883px” “height” “653px”) }}

And from the image partial:
{{ $src := resources.Get .url }}

{{/* set image sizes, these are hardcoded for now, x dictates that images are resized to this width */}}

{{ $tinyw := default “500x webp” }}
{{ $smallw := default “883x webp” }}
{{ $mediumw := default “1200x webp” }}
{{ $largew := default “1500x webp” }}

{{/* resize the src image to the given sizes */}}

{{ $tiny := $src.Resize $tinyw }}
{{ $small := $src.Resize $smallw }}
{{ $medium := $src.Resize $mediumw }}
{{ $large := $src.Resize $largew }}

{{/* add the processed images to the scratch */}}

{{ $tiny = $tiny.RelPermalink }}
{{ $small = $small.RelPermalink }}
{{ $medium = $medium.RelPermalink }}
{{ $large = $large.RelPermalink }}

{{/* only use images smaller than or equal to the src (original) image size, as Hugo will upscale small images /}}
{{/
set the sizes attribute to (min-width: 35em) 1200px, 100vw unless overridden */}}

{{ .alt }}

This will allowed me to use dict to pass image files in layouts and have them resize them for srcset=“” and include alt=“{{ .alt }}” loading=“{{.loading}}” width=“{{ .width }}” height=“{{ .height }}”

Thank you so much for your help I very much appreciate it. I am a slow learner and will get better. Thank you for taking the time to respond to me. If you see any other ways to improve this please let me know.

1 Like