On a recent Google stroll, I found an image about a GitHub repository:

Neat, where does that come from?

Neat, it’s generating them! Of course, there’s a blog post about it. And you can preview sites on https://www.opengraph.xyz/:

To be honest: I have been bothered with see my face whenever I share a blog post in Slack:

So question: How do I generated them myself?

Checking out tcardgen

A quick googeling later, I found https://github.com/Ladicle/tcardgen

go get github.com/Ladicle/tcardgen

A great, downloading half the internet again… πŸ˜…

After about 50 executions, 3h hours of tweaking and ending up with my own fork, I finally made something work.

Final structure

The command I run (after go install on my fork) is

tcardgen -f opengraph/font -c opengraph/template.config.yaml -o static/opengraphimages content/blog/2021/05/Generating\ Open\ Graph\ Images\ for\ my\ Blog.md

This requires the following files:

β”œβ”€β”€ font
β”‚Β Β  β”œβ”€β”€ My-favorite-font-Bold.ttf
β”‚Β Β  β”œβ”€β”€ My-favorite-font-Medium.ttf
β”‚Β Β  └── My-favorite-font-Regular.ttf
β”œβ”€β”€ template.config.yaml
└── template.png

The config I use is

template: opengraph/template.png
    px: 100
    py: 172
  fgHexColor: "#7B8EA6"
  fontSize: 72
  fontStyle: Bold
  maxWidth: 700
  lineSpacing: 10
    px: 100
    py: 120
  fgHexColor: "#A0A0A0"
  fontSize: 38
  fontStyle: Regular
  separator: " β€” "
    px: 120
    py: -475

I don’t use categories, I do use tags, but I don’t want them, so I move them off the canvas.

The image I use looks like this:

I really bothers me that the template can be defined in the template.config.yaml, but the font-folder cannot. But I didn’t find an easy way to add it, because I’m not a big fan of messing with go code.

Running the command creates an image in static/opengraphimages, which hugo automatically copies over to the output (public) folder.


  image: "opengraphimages/Generating Open Graph Images for my Blog.png"

in my front-matter adds the link to my page.

Next Steps

This workflow still requires running the waaaaayyyy too long command for every new post, and manually copying filenames. I would much rather

  • have hugo generate the image automagically πŸ§™
  • have hugo trigger another step which calls this ⏭
  • have a service that dynamically generates the image πŸ§‘β€πŸŽ¨
  • use svg as og:image ✨ (you can define a mime-type, but this StackOverflow entry from 2014 says svg is not allowed)


Definitely worth it:

And even Google’s “Rich Results Test” likes it:

Wow, that’s enough of my face for one day 😬 πŸ˜…