Dynamic Social Sharing Images with Eleventy

One year ago, I read Dynamic Social Sharing Images by Drew McLellan on 24ways and got super excited with the simplicity and ingeniousness of the technique.

Because it's the holidays and because I love tinkering, I sat down to try and replicate this with Eleventy. I wanted to pretty much follow Drew's steps and

  1. Render a subpage for each blog post, showing only the title for starters
  2. Style the subpage with CSS so it can be turned into a 600 × 315 pixel image (the correct aspect ratio for Facebook’s recommended image size).
  3. Screenshot all new subpages with Puppeteer

It turns out Eleventy has just the right tool for step 1: Pagination.

I created a new template file (Nunjucks) in the src folder:

data: collections.article
size: 1
alias: article
permalink: /blog/
{{ article.fileSlug }}/og-image/
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic social sharing image for “
{{ article.data.title }}</title>
<link rel="stylesheet" type="text/css" href="
{{ '/css/og-image.css' | url }}" />
<script src="
{{ '/js/og-image.js' | url }}" defer></script>
<meta name="robots" content="noindex,nofollow">
<link rel="icon" type="image/png" href="
{{ '/favicon-32x32.png' | url }}" sizes="32x32">
<figure class="ab-og-image">
<h1 class="h1">
{{ article.data.title }}</h1>
<p>a post on <a href="https://annualbeta.com">annualbeta.com</a></p>

Enabling pagination in the frontmatter tells Eleventy to loop over all items in the "article" collection (which happen to be my blog posts), use article as an alias, create one new page for each article and save it to a folder named og-image. The magic here is that {{ article.fileSlug }} will return the folder name of the current article, so Eleventy will always write the new index.html into the right parent folder.

I decided to not even bother with a layout here because the page only needs the bare minimum of markup to function. The external stylesheet contains the CSS needed to format the text, size the figure correctly and add a subtle amount of background noise.

Because the end product would be a static image at a fixed size, I decided to satisfy my typographic vanity and also wrote a bit of JavaScript to prevent single words on the last line (to be clear: I usually prioritize having less JavaScript over typographic detail for production websites, but not always – sometimes the aesthetic benefits outweigh the cost of JavaScript). Andy Bell's TypeMate was just the right plugin for that.

You can see the result for each blog post (like this one) if you append og-image/ to the URL.

That left only step 3. I like and use Gulp a lot, so I ported Drew's solution over to my build pipeline. The screenshots are saved to the respective article's folder in src, so they get copied over to the dist folder every time Eleventy runs. I chose the article's URL slug as the file name, prefixed with "og-img-". That meant I could now add social meta tags to my blog post template:

<meta property="og:title" content="{{ title }}">
<meta property="og:image" content="
{{ page.url | url | absoluteUrl(metadata.url) }}og-img-{{ title | slug }}.png">
<meta name="twitter:card" content="summary_large_image">

{# and a few more optional ones, like publication date, twitter user, … #}

A quick test with the Twitter card validator proves that everything is working:

Screenshot of Twitter's card validator, showing the social image for a blog post, saying "A changelog for my blog posts – a post on annualbeta.com"
It works!

And that's it, folks!


  • Dec 29, 2019 Try to clarify my view of aesthetic details vs. cost of JavaScript
  • Dec 29, 2019 Fix the example link for a generated sharing image subpage
Post a comment If you post a tweet with a link to this page, it will appear here as a webmention (updated daily).


  1. Lee Clissett Lee Clissett
    awesome. any chance you'll make the repo for your blog public any time soon?
  2. Søren Birkemeyer 🦊 Søren Birkemeyer 🦊
    That's a yes! It's on the list of things to do next decade. 😅Probably sooner rather than later – I'll ping you. 📣
  3. Timothy Miller Timothy Miller
    I was *just* thinking today it would be fun to do this. Lo and behold! Instructions!
  4. Jared White Jared White
    That's a really cool technique! OK if I steal the idea for another SSG? 😃 (with proper attribution of course)
  5. Søren Birkemeyer 🦊 Søren Birkemeyer 🦊
    Nice of you to say! Go ahead, as I said in the article: I already "stole" the idea from @drewm's excellent 24ways entry, I just wrangled it into my 11ty setup. 😉
  1. liked by Mark Boulton
  2. liked by Eleventy
  3. liked by The Realest
  4. liked by Jared White
  5. liked by Jens Grochtdreis
  6. liked by Marc Stalfoort
  7. liked by Ahmad Al Maaz
  8. liked by Eduardo Uribe
  9. liked by Matt Biilmann
  10. liked by ⚡️ Mark Buskbjerg ⚡️
  11. liked by Eleventy
  12. liked by Will ᕙ(⇀‸↼‶)ᕗ
  13. liked by Brian Z
  14. liked by Scott McCracken
  15. liked by AJ Klein
  16. liked by M2 🚀
  17. liked by Dan Ciupuliga
  18. liked by jason
  19. liked by Nick
  20. liked by ross
  21. liked by Gaël Poupard
  22. liked by Michael Milz
  23. liked by Jens Grochtdreis
  24. liked by David Hund ✌
  25. liked by Christophe Hollebeke
  26. liked by Nauval Rizky
  27. liked by Manuel Matuzović
  28. liked by Jimmy Hugge
  1. reposted by Eleventy
  2. reposted by Will ᕙ(⇀‸↼‶)ᕗ
  3. reposted by Eleventy