Generating OG Images in Elixir
I recently added per-page OG images to this blog and it was less complicated than I expected. On top of that, I was able to completely stay within the Elixir ecosystem! If you’re not familiar, OpenGraph image meta tags let you define an image that should be rendered as a preview for the page, which is used in social media and messaging apps.
So let’s start with the requirements: I wanted a low effort approach to creating per blog post OG images, and that new posts should automatically get images without any additional work. Additionally, I wanted matching images for pages like /about. I’m using NimblePublisher to render markdown to HTML at build time and I wanted to follow the same principle for the OG images.
Image library
Step one was figuring out how to generate these images on the fly. The JS community has takumi-js and satori that take a basic HTML template and render it to an SVG/PNG using the Yoga layout engine. This is very convenient because you can write a HTML/CSS and get an image out of it, and it’s fast enough that you can generate images ad-hoc.
import { render } from "takumi-js";
import { writeFile } from "node:fs/promises";
const image = await render (
< div tw = "w-full h-full flex items-center justify-center bg-gradient-to-b from-blue-100 to-red-50" >
< h1 tw = "text-6xl font-bold" > Hello from Takumi
,
{ width: 1200, height: 630 },
);
await writeFile ( "./output.png", image );
Now, you could shell out to node to use either one of those libraries and that would probably work fine for me, I already have nodejs in my build environment anyway, but where’s the fun in that! With some minimal digging I found the incredible Image library by the prolific Kip Cole.
Image uses libvips under the hood, but will automatically pull that in for you at build time, so you don’t have to worry about it. The library offers an incredible amount of functionality, of which we’ll just be using a tiny bit. However, it does not have an…