Here’s a meta post for you, if there ever was one: if someone shared this post with you and a preview image showed up, that image was generated by the thing this post describes. The post is its own demo. I find that unreasonably satisfying.
It looks like this, generated live from the post’s own title and description as you read this:
(If I change the title, the image changes with it. There is no image file.)
Every post on this blog has been sharing the same static banner.jpg since I set the thing up (not because I picked it with care, mind you, but because that’s what happens when you configure a Hugo theme once and never look at the og:image tag again). Meanwhile, Proper™ blogs have those per-post preview cards with the title in big friendly letters. I wanted that. What I did not want was a build step generating and storing hundreds of PNGs.
Then I remembered codimg, the little Go (+ Elm for the playground, and because I couldn’t help myself) service from a couple of months back that turns code into syntax highlighted SVG. Completely stateless; the code lives inside the image URL. So a per-post preview image is just a URL, and Hugo can put URLs together at build time just fine.
So what does a blog post look like as code? Like this, obviously:
{- cekrem.github.io/posts/[slug]
-}
title : String
title = "[title]"
description : String
description =
"[description, wrapped at 56 characters and joined "
++ "with (++) like a civilized person would]"
(With an ASCII art banner on top, naturally.)
A Hugo partial builds that snippet for every post, URL-encodes it with querify, and drops the codimg URL straight into og:image and twitter:image. When a crawler asks for the image, codimg renders it on the spot.
A couple of confessions. codimg accepts deflate-compressed base64 input to keep URLs short, but Hugo can’t deflate anything at template time, so I’m sending plain URL-encoded Elm. Roughly 2KB per URL. It’s fine. Also: I wrote a word-wrapper in Hugo’s Go templates to break long descriptions into that ++ chain. I’m not proud of it. (I’m a little proud of it.)
So will it actually work? On some platforms it will! Though a few platforms simply disregard banners in the svg format, it’s honestly better with their fallback text + description in plain text than that generic blog banner.
And it was fun 🤓