To be honest, the switch from a classic CMS to Hugo had a steep learning curve at first, but Hugo is incredible. Even though we have static pages, you can do a lot of “dynamic” stuff.

The Problem

From the start when I began using Hugo, I had an issue with the open graph images, per default you can specify one image as default in hugo and use post header mages as og:image. Well - I try to keep my blog posts free of images which are not beneficiary to the content. Screenshots are fine but general pseudo matching placeholder images just that you have something for the header 🤷‍♂️. So the problem that came up was all posts ended up with the same image when you linked to the article.

The Solution

I found an Article from aarol who had similar issue some years back. Thanks for writing about it! Thats the reason I’m doing this post, it might help someone else avoid the same.

The Idea is simple: Instead of having a dull placeholder we can use hugo image manipulation functions to automatically create an fitting og image for each post. You specify an base background and it automatically take the post title and write an og.jpg to the filesystem and insert the meta accordingly.

How to ?

I Link to the Original Article from aarol here as base. I changed and updated it for my implementation a bit.

  1. Partial override for the opengraph template. Always take the latest version from here before you start and put it into:

    layouts/partials/opengraph.html

  2. I removed the original static / header based og:image part from it completely because I dont want that. Afterwards I added this modified code:

{{/* Generate opengraph image */}}
{{- if .IsPage -}}
    {{ $img := resources.Get "og_base.jpg" }}
    {{ $Font := resources.Get "/Lexend.ttf"}}
    {{ $img = $img.Filter (images.Text .Page.Title (dict
    "color" "#ffffff"
    "size" 58
    "linespacing" 2
    "x" 42
    "y" 272
    "font" $Font
    ))}}
    {{ $img = resources.Copy (path.Join .Page.RelPermalink "og.jpg") $img }}
    <meta property="og:image" content="{{$img.Permalink}}">
    <meta property="og:image:width" content="{{$img.Width}}" />
    <meta property="og:image:height" content="{{$img.Height}}" />

    <!-- Twitter metadata (used by other websites as well) -->
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="{{ .Title }}" />
    <meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
    <meta name="twitter:image" content="{{$img.Permalink}}" />
{{ end }}
  1. Different to Aarols implementation I simplified it and only use one generic font and block for the Title only. I just want an eye Catcher where display of the page.Title is enough. But beside that same same.
  2. Re-build you page and you should see all the og.jpg images created.
  3. As a tip, make sure your base image isn’t to large or all generations will be even larger. If you just use a plain background use png, otherwise if you use a picture like in my example use a compressed jpg.

The Result

The Actual generated result for this Article will look like this:

Automatic generated dynamic opengraph ogimage in hugo

And Everytime a page link to one of my articles is shared somewhere the opengraph protocol is used, it will look like this Example from a Paste to Discord:

Discord example, dynamic ogimage from hugo