Recently, I was scrolling through Twitter and saw this post:

The first thing I thought was: "dang, that's a pretty nice cover image." I scrolled a bit more and saw another post that had a similar cover image. Now I was curious. These seem to be automatically generated for every blog post.. I wonder how I could add that functionality to my blog?

Beginning, I googled how to add text to images using NodeJS and came across Jimp. It seemed like a pretty simple task, so I immediately got down and started making a base "theme" for the post to use. I came up with this:

Blog cover template

Now that I had the basic theme, I began hacking away with Jimp.

Step #1- Write something on a PNG image

This first iteration worked surprisingly well. For a basic proof of concept, I got the following image:

As far as the core went, all worked well! I was ready to move on to making it create a cover photo when an HTTP request (GET) is sent.

Why a GET request instead of a POST request?

In this first iteration, I simply wanted to make it work by setting the OG image tag to a dynamic URL that will auto-generate the cover photo. My initial thought was something like "/social-cards?title=My title here&author=Kenton Vizdos&time=3 minutes&slug=my-title-here"

Step #2- Adding the REST API

Overall, my routing system was pretty much just filling in some boilerplate stuff into my pre-existing routing code. The only thing I changed in the code above was changing the hard coded values into dynamic variables

(p.s. the Express boilerplate code I created a while ago is pretty old and I'm in the process of rewriting it, so don't judge it too hard ?)

Now, my HTTP request works! Awesome, now on to setting up some Ghost features..

Browse the code at this milestone ?

Contribute to kvizdos/auto-blog-covers development by creating an account on GitHub.

Step #3- Configuring Ghost

At first, I thought this would be the easiest step. I'd simply set the URL of the Twitter and Facebook card to the HTTP API I setup..

As I quickly learned, Ghost removed that feature a few versions ago :( Now, I sadly needed to figure out a hacky way of getting around it.

Luckily, I created my own theme for this blog, so I have full control over what goes where.

In my default.hbs file, I simply added the following into the header after the {{ghost_head}}:

    {{#is "post"}}
        <meta name="og:image" content="{{post.title}}&slug={{post.slug}}&author={{}}&time={{post.reading_time}}">

        <meta name="twitter:card" content="summary_large_image">
        <meta name="twitter:image" content="{{post.title}}&slug={{post.slug}}&author={{}}&time={{post.reading_time}}">

Sadly, this does mean that I can't have any different cover photos, but I'll live with that issue as it's also one of the main reasons I made this: consistent branding.

If you put this code before the ghost_head, it won't work as the ghost_head overrides some of the values, even if you don't set a cover image (mainly, it replaces "twitter:card" contents to "summary" instead of "summary_large_image" so there would be no photo added.

There definitely are workarounds to make custom covers work, but it wasn't an issue for me, so I went with the current way.

View the theme code here:

This contains my main portfolio site,, and my custom Ghost theme for my blog at - kvizdos/Portfolio

Step #3- Change & optimize the card template

In the beginning, my cards template was 1920x1080px, but I realized that I didn't need them to be that large and that they would take up quite a bit of space on my server.

I began researching the best size for a Twitter summary image, but I couldn't find one solid number. I ended up going with 1200x630px because it had enough clarity when viewed while still having a decent file size.

Once I got this new number, I used my little design skills to recreate a card and ended up with this:

It's not perfect by any means, but I made it and I'm happy with it, so it's good enough :) Feel free to use it as well.

I also had to change up my rendering code to reflect the new design. The latest code can be found here:

Contribute to kvizdos/auto-blog-covers development by creating an account on GitHub.


In the end, I really like how this system works. It's not perfect, but it works. One big issue that I plan on fixing soon is that anyone with the URL can create an image. I'd rather not let anyone generate an image on my server, so I am going to be converting this to a webhook-based system to create the image.

However, as a proof of concept, this system works really well. There are some limitations (mainly, no #'s in the post title!), but I'll learn to live with them.

Another great reason to use this is that if you ever want to change the way all of your social media cards look, you just need to update the card template PNG and then delete the files on your server. Simple as that. Any blog will automatically be updated (when they are tweeted again/a tweet with the URL is viewed). Or, you can just update the template file and only have new posts get it if you don't delete the images.

With that, you can view all of the code here:

Contribute to kvizdos/auto-blog-covers development by creating an account on GitHub.