Jan 23, 2023
Introduction
I'm in the process of rebuilding this website using eleventy and I wanted to get fancy with this revamped site.
One feature I wanted was a custom Open Graph social share image for each page on the site. If you're not familiar with Open Graph, check out this explanation. In a nutshell, when you share links to the site on social networks, it should an image to draw people into the page.
Eleventy has a pretty robust ecosystem already and lo and behold, there are already plugins to handle this for you. Great, let's just check them out.
What's out there?
eleventy-plugin-social-img pre-generates cards using a shortcode. I spent about 30 minutes trying to grok the readme and how to use the plugin. I like that you could create an HTML template, but the plugin looked overly complicated for what I needed.
@resoc/eleventy-plugin-social-image only works with Netlify and requires a server component. This site is hosted on GitHub Pages and is fully static. Pass.
eleventy-plugin-social-share-card-generator requires a Cloudinary account. Pass.
eleventy-plugin-og-image looks promising, pre-generates images, and looks pretty easy to use. This could be a runner-up if I ever want to go a different route.
All in all, I wasn't a huge fan of these solutions. There had to be something simpler.
Something simpler
The eleventy team offers various API Services for the public to use. Screenshots is a runtime service that takes a screenshot of a URL and returns that image (and caches it). Bingo!
All I need to do is create a simple template and style it however I'd like for my social sharing card. Apply the template to every page in the site. Then the service can generate screenshots for my blog posts on demand when somebody shares a link to my site.
Creating the card template
To make it easy to manipulate URLs, I would have a mirrored set of paths at https://patricklee.nyc/social/<page URL>
. Then I simply embed that URL to the screenshot service API URL and it would return an image.
Example of the social card webpage:
https://patricklee.nyc/social/about
Example of the social card screenshot image generated by the service:
Note: You need to URLEncodeURI the page being passed into the API
I created the simplest webpage possible, you can see the template here.
- I wanted a simple design with the title of the page, the URL of this site, and a picture of me.
- I created a 1200 x 630 pixel box, then added my card contents in there. The screenshot service accepts a size parameter
opengraph
which will take a 1200 x 630 pixel screenshot.
Generating the pages
Next, we need to generate the pages on the build. To do this. I created a page social.md
with some basic frontmatter. See the page here.
---
layout: social.liquid
pagination:
data: collections.all
size: 1
alias: post
permalink: "social//"
---
This page uses a combination of collections.all
, pagination
, and permalink
to force eleventy to generate a page for every page on the site under the /social/
namespace.
Now when we want the social image for any page we simply add /social/
to the URL.
For example, the page https://patricklee.nyc/about
has a social image at https://patricklee.nyc/social/about
.
Updating the Open Graph tags
The last part is to update the Open Graph tags to use this image. In my header, I had my Open Graph social tags available. I just needed to create a URL that would take each page's URL, and append /social
to it, then embed it into the screenshot service's URL scheme.
To make things dead simple, I just created a shortcode that would generate this URL when used on a page.
// eleventy.js
eleventyConfig.addShortcode("openGraphScreenshotURL", function () {
// URL Encode the page
const encodedURL = encodeURIComponent(
`https://patricklee.nyc/social${this.page.url}`
);
// Generate a cache-busting key for quicker testing
const cacheKey = `_${new Date().valueOf()}`;
// Return the screenshot service's URL to add to the open graph tags.
return `https://v1.screenshot.11ty.dev/${encodedURL}/opengraph/${cacheKey}`;
});
Then I updated my header to use this shortcode under the og:image
and twitter:image
tags like so.
<!-- header.liquid -->
<meta property="og:image" content="https://v1.screenshot.11ty.dev/https%3A%2F%2Fpatricklee.nyc%2Fsocial%2Fblog%2Feasy-dynamic-social-sharing-image-with-eleventy%2F/opengraph/_1728070925785" />
<meta property="twitter:image" content="https://v1.screenshot.11ty.dev/https%3A%2F%2Fpatricklee.nyc%2Fsocial%2Fblog%2Feasy-dynamic-social-sharing-image-with-eleventy%2F/opengraph/_1728070925785" />
That's all it takes!
Conclusion
The risk here is if the eleventy team decides to sunset the screenshot API, I'll need to switch to a solution that pre-generates the social share images or host the API myself. Considering how little traffic this site gets, I'll take that risk for reduced complexity now.
I like this solution because I came up with it because it is simpler, doesn't require pre-generating all the images upfront, and doesn't require adding more packages. Oh and because I was able to offload a lot of the work to the wonderful devs at eleventy. Thanks, ya'll!