How I built dynamic social media images in Eleventy using Cloudinary

For this blog, I have added dynamically generated social media images (think: <meta property="og:image" ...) based on the title of each post. There are many elegant solutions out there for this.

I didn't use any of them.

This is a quick and dirty way to achieve this. Though this was also the easiest way for my brain to wrap itself around the problem.

Assumptions

Pros to this approach

Cons to this approach

TL;DR


<!-- in your <head> partial -->
<!-- if we're on the homepage... -->
{% if './src/index' in page.inputPath %} ...
<!--  ... use a default social image  -->
<meta
  property="og:image"
  content="https://res.cloudinary.com/path/to/default/socialimage.png"
/>
{% else %}
<!-- if not on the homepage, use include the ogimage partial -->
{% include './ogImage.njk' %} {% endif %} 

Then we have a stand alone partial called ogImage.njk:


<!-- in ogImage.njk -->
<meta
  property="og:image"
  content="https://res.cloudinary.com/chipcullen/image/upload/c_fill,e_negate,h_630,w_1200/c_fit,g_west,h_630,l_text:SourceSerif4Bold.ttf_70:{{ title | urlencode }},co_white,w_1000,x_20,y_20/g_south_west,l_text:Roboto_35:ChipCullen.com,co_white,x_20,y_40/v1669146466/pthalo_blue_texture_fyc8cy.png"
/>

This template contains a reference to a Cloudinary URL that injects the title of our social post as an overlay, which the Cloudinary API will impose, on-the-fly, into a final image.

How did we get here?

Getting Started with the Cloudinary Editor

As of this writing, in Spring of 2025, the Cloudinary Media Library looks like this:

Find the image you want to server as your background, and access it's advanced editor:

The Cloudinary Library with a menu open and the Advanced Editing feature selected

Which then takes you to the advanced editor itself (note in this image I've already set an "Effect" of "Negate", which is like the Photoshop "Inverse" command):

The Cloudinary advanced editor with the negate effect applied

(I think this is in actually the old school Cloudinary editor, and the default editor has been updated. However, the kind of operations we're doing are really only possible with the "Advanced" editor.)

In the editing controls, you will want to set the width to 1200 and the height to 630. Here are the OG image recommendations from Facebook.

Here is what my image looked like with the initial transformations applied:

The cloudinary advanced editor with width, height, and effect options set.

The important thing to note here is how the URL output updates, like so:

https://res.cloudinary.com/youraccount/image/upload/c_fill,e_negate,h_630,w_1200/v1669146466/filename.png

We're going to rely on the URL api for our implementation overall.

Adding an Overlay

The actual text in your social image will be rendered as an "Overlay". So, you want to add an overlay with this button:

Pointing out the overlay button

You then specify everything about the type itself, including the text content, with the Image ID input:

There is a syntax for how you specify a typeface and the text itself.

The thing to note is that this updates your image url with a new l_text string. It will look something like:

...,l_text:typefacename_size:Text you want

Separately, you can set the text's position with the Gravity setting, and an offset from the sides with the other options below.

Typeface choices

The documentation on what typefaces are available is confusing. It says most system fonts, and all Google Fonts, are supported.

(One pro tip - Google Fonts with spaces in the name use an _ for the spaces. e.g. Open_Sans.)

However, in the comments at the bottom of that page, they indicate that only popular Google Fonts have been added. So, it's a bit of a guessing game whether your desired font is supported.

In my case, I found that one typeface was not supported (Source Serif 4) and one was (Roboto).

I had to upload a version of Source Serif 4 in order to use it in my social graphics. That's a whole other procedure, and I'm not going to get into it now.

Experiment!

For this part - I can offer little guidance. You will need to play with the Cloudinary API itself and see what results that you can get. Keep in mind that there are quite a few styling options available to you.

Inserting blog post titles on the fly

Once you have a text overlay set up just the way you want, it's just a matter of creating a template that inserts your title. You can do that in nunjucks syntax with {{ title | urlencode }} . That looks like this in my template:


<meta
  property="og:image"
  content="https://res.cloudinary.com/chipcullen/image/upload/c_fill,e_negate,h_630,w_1200/c_fit,g_west,h_630,l_text:SourceSerif4Bold.ttf_70:{{ title | urlencode }},co_white,w_1000,x_20,y_20/g_south_west,l_text:Roboto_35:ChipCullen.com,co_white,x_20,y_40/v1669146466/pthalo_blue_texture_fyc8cy.png"
/>

Which, for this blog post, results in:

The OG image for this post