How to Dynamically Generate on-brand Social Sharing Images in NextJS

Back to blog

How to Dynamically Generate on-brand Social Sharing Images in NextJS

12/23/2021

Social sharing images are the pictures that accompany content when a link is posted on a social media service. Let's take a look at an example, using this blog post:

A screenshot of this article being shared on Discord

This is what this blog post looks like when shared on Discord. I've included some branding using my colour palette and logo, followed by the article title in the same font I use for headings on this site. My intention is to create an image with strong branding to try and make myself look really legit and competent and trustworthy.

If you're producing content that you want others to read and share, social sharing images play a critical part. Good, on-brand imagery helps draw potential readers into your content, and can help provide a nice introduction into your brand. First impressions count!

Social sharing images form part of the Open Graph protocol, which is essentially a set of metadata you can insert onto to a web page to add extra context to your links when they are shared on social media. There's loads of metadata specified by the Open Graph protocol that you can choose to add to a page, but there's only four required pieces for social sharing, generally speaking. In this article, I'll be focussing on the og:image property, which is what is used as the image for your content when it's shared on social media.

It's entirely possible to manually create imagery to use with your posts, but - like any good programmer - I hate doing things manually when I could tell a computer to do it for me. I therefore decided to do what is known as a sick programmer move and spend hours automating a task that would take minutes to do manually each time.

I've built a simple system to generate the social sharing image for me whenever I post a blog post. I think it's pretty cool, so let's go over how I built it. Please bear in mind that this is a NextJS-centred solution, so if your're using a different tech stack you might need to adapt these steps to fit your use-case.

Step 1: Prepare your project for social sharing images

First, we need to prepare a folder structure to save images in. I did the following:

  1. Create a folder for social sharing images: my image folder lives in /public/images/og within my project. I put it here because NextJS supports static file serving for anything placed inside /public.
  2. Update your .gitignore to not track anything placed inside your social sharing image folder:
# Other gitignore rules

/public/images/og

I've added the folder to my .gitignore because these images will be generated at build time, and I don't want to bloat my git repo with unnecessary images.

Step 2: Create a background

One very, very important note about social sharing images: they need to be 1200 × 630 pixels. Anything not following that aspect ratio will look really weird and stretched when shared.

I created a simple background image in Figma to use as a base for my social sharing images. Here's what that image looks like:

Social sharing image background

I've decided to keep it simple because I want to insert the title of my blog posts into the image, on top of the background.

When this image is ready, put it somewhere in your repo that's easy to access and remember. I put mine into /public/images.

Step 3: Create a bitmap font

I use a library called jimp to write the text onto the social sharing images. It's an image manipulation library written in JavaScript and I found it really easy to use.

One important caveat, though. jimp comes with a print plugin for writing text into an image, but this plugin requires a bitmap font to work. I used ttf2fnt to export the font I use as a bitmap font. You can use ttf2nt with any of the fonts available on Google Fonts.

After you've created your bitmap font, put it somewhere in your repo that's easy to access and remember, just like the background image from the step above. I put my font into /public/font.

Step 4: Set up jimp

With the font and image ready, we can now write some code to get jimp up and running. There is a three-step process to get jimp to create social sharing images for us:

  1. Load the background and the bitmap font
  2. Create a new image by writing the article's title onto the background image
  3. Save the new image somewhere

To achieve those three steps, I wrote a function. I've annotated my code, so hopefully it's not too difficult to understand what's going on:

const jimp = require("jimp");

const FONT_FILE = process.cwd() + "/public/font/ogImageFont-large.fnt"; // path to our font file
const IMAGE_DIRECTORY = process.cwd() + "/public/images/og"; // path to our image directory
const BG_IMAGE = IMAGE_DIRECTORY + "/ogImage-bg.jpg"; // our background image

// production URL for our website, because our social sharing images need a full
// path to work
const URL = "https://www.scanlon.international";

// some positioning constants so we can tell jimp where to put the text on the background
const IMAGE_WIDTH = 1200;
const IMAGE_HEIGHT = 627;
const TEXT_SIZE = { x: IMAGE_WIDTH - 256, y: IMAGE_HEIGHT - 192 };
const PADDING = { x: 128, y: 192 }; // align the text with the logo in my background image

/**
 * Creates a branded social sharing image to be used with the og:image property.
 *
 * @param {string} title The title of the blog post. This is the text that will appear in the social sharing image.
 * @returns {string} The URL of the location the social sharing image has been saved to.
 */
const generateOgImage = async (title) => {
  const slug = title.split(" ").join("-"); // The "slug" is used as the filename of the generated image.

  // Load in the font and background image for jimp.
  const font = await jimp.loadFont(FONT_FILE);
  const bg = await jimp.read(BG_IMAGE);

  /**
   * Inserts the text onto the background image.
   *
   * @see https://github.com/oliver-moran/jimp/tree/master/packages/plugin-print#print
   */
  bg.print(
    font,
    PADDING.x,
    PADDING.y,
    title.toUpperCase(), // Headings are uppercased on my site, so I make it uppercase here to be on-brand
    TEXT_SIZE.x,
    TEXT_SIZE.y
  );

  // Save the newly-created image.
  bg.write(IMAGE_DIRECTORY + `/${slug}.jpg`);

  // Return the full URL to the newly-created image, for use in the og:image meta tag
  return `${URL}/images/og/${slug}.jpg`;
};

export default generateOgImage;

Basically, this code creates a image with the title of an article and then saves that image into the image folder we created earlier. I return the URL to the image within this function because it's used within NextJS's getStaticProps() to populate my metadata at build time.

At this point, we have a social sharing image created and ready to be used. At this point, my social sharing image looks like this:

The completed sample social sharing image, ready for use

That's great, but how do we actually make this image visible when we share our articles?

Step 5: Use jimp within nextJS

With jimp up and running, we can now use it within NextJS. The aim here is to get the URL of the image, returned by our generateOgImage() function, and insert it into the article's metadata under the og:image property. At that point, it will be visible when articles are shared over social media.

This is a simplified version of what the code to generate the page and metadata looks like:

const BlogPost = ({ post, meta }) => (
  <PageLayout title={post.title}>
    // Page metadata, which includes the social sharing image
    <Head>
      <meta name="description" content={meta.description} />
      <meta property="og:title" content={meta.title} />
      <meta property="og:image" content={meta.image} />
    </Head>
    // Article is rendered here
    <PostBody title={post.title} date={post.date} content={post.content} />
  </PageLayout>
);

export async function getStaticProps({ params }) {
  const post = getPostBySlug(params.slug); // Returns a JS object which includes an article's content and title

  const content = await generateContent(post.content || ""); // Creates the article's content as HTML
  const ogImage = await generateOgImage(post.title); // Generates the social sharing image and returns the URL to the image

  return {
    props: {
      post: {
        content,
      },
      meta: {
        image: ogImage, // Include the social sharing image in the page's metadata
        title: post.title,
        description: post.excerpt, // A short description about the article, which will be visible alongside the image when social sharing
      },
    },
  };
}

We first call generateOgImage() within NextJS's getStaticProps(), and then pass the URL to the image into our component via props. Then, on the page itself, we insert the social sharing image (and other metadata) using NextJS's <Head /> component which allows us to insert the meta tags onto the page's <head>, where social media services will look to find Open Graph properties.

Everything should be working now! When we write a blog post, we can include a title, which will then be used within our getStaticProps() to auto-generate a social sharing image for us, and include that image as a <meta> tag in the page which shows the blog post. It takes a while to wire up, but works flawlessly.

One very cool bonus of this approach lies within the specifics of how getStaticProps() works. getStaticProps() will only run when you build your NextJS site, so there's no need to worry about the performance impact of using a javascript library to generate the images. By the time your site is deployed, all of your social sharing images will have been generated and will stay until your next deployment.

Other Approaches

This is just one valid approach of many to create social sharing images. Here's three other options to explore if you (understandably) dislike my jury-rigged approach:

To Conclude

To close this post I want to reiterate the importance of using Open Graph tags in your content. Without them, any links that are shared to your stuff will look boring and uninviting. You might have a hard time attracting visitors to your site. This may not be a huge problem depending on what you're building, but if your product or your employer's product relies on social media buzz for marketing, it's absolutely critical that you know what OG tags are and how to use them.

I'm not expert by any means, but my journey in trying to make my portfolio look interesting when shared (primarily on LinkedIn) has taught me a lot about what Open Graph tags are, and why they're important.