Adding Social Media Images to a Gatsby Site
Tags: javascript, gatsby
Published: 2024-05-18 03:34:40

---
title: Adding Social Media Images to a Gatsby Site
published: true
date: 2024-05-17 00:00:00 UTC
tags: javascript, gatsby
canonical_url: https://jvarness.blog/adding-social-media-images-to-gatsby/
---

When you post links on social media, sometimes the links generate a card that looks really really nice. I say sometimes because… Well… My blog never had good ones. I basically took that little “JV” logo I made in Gimp and set that as the image. It _worked_, but it wasn’t _great_.

When my links get posted online, I wanted to make sure that they looked at least somewhat professional. I may be the only one ever posting them, but I at least wanted them to look good! Not just a couple letters.

So today, I’m going to tell you about the methods I researched to be able to add social media images to my site, and how I landed on using a Gatsby plugin called [gatsby-plugin-satorare](https://www.gatsbyjs.com/plugins/gatsby-plugin-satorare/).

## Alternative Solutions

Let’s start off by pointing out how others solved this: there are quite a few people who have solved this issue in the past, and many of the solutions involve a similar process: build a component using React that you will use in tandem with puppeteer to be able to render the images in a browser window and then have it take a screenshot of the preview page. Here were some of the posts that I had found:

- [Emma Goto - “Generate custom social card images for your Gatsby blog”](https://www.emgoto.com/gatsby-react-social-card/)
- [Dan Spratling - “Automatic social images in Gatsby”](https://danspratling.dev/blog/automatically-generating-a-social-image-in-gatsby)
- [Max Poutard - “How to generate social share images with Gatsby”](https://www.maxpou.fr/blog/generate-social-image-share-with-gatsby/)

These solutions work just fine, so if for whatever reason you don’t like the way that I did this, you could try one of these. These solutions do have an easy way for you to test out that your generator is working. The solution I used did not have the easiest way of testing out that things worked, but once I got it working it’s smooth sailing.

In doing the research for which plugin/solution I ultimately wanted to use, I went to search for plugins on Gatsby for social images, and`gatsby-plugin-satorare` came up as a potential option. It uses another library from Vercel called [`satori`](https://github.com/vercel/satori) that can take JSX syntax and convert it into an image. They also have this really great[Playground](https://og-playground.vercel.app/) where you can take the code you want to use to generate the image and you can test out how the end product looks. If I were you, and you want to use `gatsby-plugin-satorare`, I would probably start there.

## The OG Image Playground

Vercel’s OG Image Playground uses `satori` to be able to generate images using JSX syntax. However, this library does have it’s limitations in terms of the types of styling that it supports. Before diving into this, I would recommend taking a look at the [list of supported CSS properties](https://github.com/vercel/satori?tab=readme-ov-file#css)that you can use to create an image. One of the things I ran into when I was doing this was that border styles weren’t working the way I expected them to.

My blog uses border styles quite a bit to make some of the elements look like they have slanted edges. For example, this is the style used in a couple of different places on the site:

```
border-left: 3em solid transparent
border-bottom: 3em solid #e5e02d
```

By using combinations of `border-*` styles and defining one as a solid colored border and the other as a solid transparent border, you can cut a little of the colored border off on either side. I wanted to use this type of styling in my images, but `satori` doesn’t have the ability to do this when it’s generating the images.

If you wanted to try playing around with this effect, try heading to this [codepen](https://codepen.io/jvarness/pen/ExJqKVL).

If you were to try this same effect in the playground, however, you would notice that this simply doesn’t work. I’m assuming this is because of how satori works: it converts JSX syntax into an SVG, and I’m guessing that the translation from CSS styles to SVGs isn’t perfect.

The workaround I used was that I just essentially created a little box that was filled with a black color that would cut off a little piece off the end of the date line on my images, and that ends up working ok.

If you want to see the actual HTML/JSX I’m using to generate the image, I was able to create a shareable link on the [OG Playground](https://og-playground.vercel.app/?share=nVXfT9swEP5XLE-TNqk0TWgHimAPbEOaNKRp8JiHOvUlMTh2ZDtAV_V_n52fdVu2QV_q3J3Pd9_3-bzBK0kBx_iCssdEIKTNmsPlZuPWCFGmK07WMVpmHJ6Xk9bq1l-ZgpVhUljfSvK6FL33iVFTWGsYzWbVsKcAlhfGmj-d7lhTsnrIlawF_SK5VDFKcMqtLcF9gFQUGruWnFEULqpn9A4WMIvoXtAvQlmtXWhkgwZnRShlInf2UyhROF1AOTgzKcw1KRm3LSb4ijPxcENWt2ttoLy2vi5wu03EZ7e4KMIBIWTbbkse62kS3rLf4Mxzd5Dd2-xEaLO8NXWWLW0umygowi5j9P8ZbQN-xhupwG7fTRu1aTs69wndoTTBjscBih7GO1n1WLWQ76M9cjkbLB3nCT5bvB8CHWoIdbXaPvvg_YrQ2LdPfkOf1KyVWYIVcGLYI3gBqTRGls4dTs92uXW_kqiciR-Q2WoT7FHf099jG3m-tvaheoR-SqsJiqRAN2SN5qbo2uoB34X8WIseuLaODtzDhgco94vtUT_0GEWEzqRqUFDSEAMf5gsK-ccXoSSpPb82PpSmre5kbm-o5-AdgOfuRh9D6SKwzbfCG1dvkGDLV0Om1-M_Su81MApS7Su0xf9qEEtk58hxfb9BtodXBCHCWS6-2znSjCTX5wkIP-QV8rt_JEqA1tOUy_xQeqzsjLZMtbpMcGFMpeMg8PYFrCQ56KBca-DZ9L6CPMHeFb7chNF5d3avON_2krLH6buYjSPgb5weDvdjs33EIvD01f7jCZaVE4bG8QY3LeDYPTwT3BaPY_veTDCFtM5xnBGuYYKhlPfsbl25d888NV82kWPjW5kCxbFRNWwn2JDURhTAuXySilO8_QM) where you can see it.

Once I had the image working, it was time to configure the plugin!

## Configuring `gatsby-plugin-satorare`

The first thing you’re going to want to do is create a small JavaScript file that acts as the “component” of the image. I say “component” because it’s not so much a React component as much as it’s just something that uses JSX syntax to define how the image is supposed to look.

You’ll want this file to `export default` a `function` that takes an argument that is a Gatsby `Node`. You can change the behavior of how the image is rendered based on the type of node it is. In my case, all of my blog posts are generated using the [`gatsby-transformer-remark`](https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/) plugin, so they are all of type `MarkdownRemark`. I decided not to generate images for all of the pages yet, so I’m only configuring the plugin to target`MarkdownRemark` nodes. In the end, this is how my little component looked:

```
// src/components/social-image-card.js
export default function (node) {
  const postedDate = new Date(node.frontmatter.date).toLocaleDateString(
    undefined,
    { month: "long", day: "numeric", year: "numeric" },
  )
  return (
    <div
      style={{
        display: `flex`,
        flexDirection: `column`,
        width: `1200px`,
        height: `630px`,
        backgroundColor: "black",
        border: "solid 15px #e5e02d",
        borderRadius: "25px",
        padding: "3em 1.5em",
        fontFamily: "BlinkMacSystemFont",
      }}
    >
      <h1 style={{ color: "#e5e02d", fontSize: "4em" }}>
        {node.frontmatter.title}
      </h1>
      <h2 style={{ color: "#e5e02d", fontSize: "3em" }}>
        {node.frontmatter.description}
      </h2>
      <div
        style={{
          display: "flex",
          borderTop: "3em solid #e5e02d",
          height: 0,
          width: "75%",
        }}
      >
        <h2
          style={{
            color: "black",
            position: "relative",
            bottom: "1.75em",
            marginLeft: ".5em",
            fontSize: "2em",
          }}
        >
          Posted on {postedDate}
        </h2>
        <div
          style={{
            borderTop: "5em solid black",
            width: "5em",
            height: "5em",
            transform: "rotate(45deg)",
            position: "absolute",
            top: "-40px",
            left: "800px",
          }}
        ></div>
      </div>
      <div
        style={{
          display: "flex",
          margin: "1em",
          position: "absolute",
          bottom: 0,
          right: 0,
          borderBottom: "2px solid #e5e02d",
        }}
      >
        <h2
          style={{
            color: "#e5e02d",
            alignItems: "flex-end",
            fontSize: "2em",
          }}
        >
          jvarness.blog
        </h2>
        <img
          src="https://jvarness.blog/images/myself.jpeg"
          width={128}
          height={128}
          style={{
            borderRadius: "50%",
            margin: "1em",
            border: "solid 5px #e5e02d",
          }}
        />
      </div>
    </div>
  )
}
```

As you can see here, it’s a little different from my example code in the OG Playground, but the key differences are mainly that it’s templated using the `frontmatter` contained within all my blog posts. Other than that, it’s pretty much exactly what I put in the playground.

One thing that is important to note here as well: you may notice that for the whole component I’m using a font style `fontFamily: "BlinkMacSystemFont"`. In order to get a custom font into the plugin to resolve correctly, you’ll need to download the fonts you want to use. I was abel to find these fonts in [this github repo](https://github.com/aliifam/BlinkMacSystemFont) and I was able to get it in `.otf` format. I’m not sure if that matters or not, but the author of the satorare plugin was using `.otf` in their example, so that’s what I did.

Anyway, now that I’ve got the fonts I wanted to use _and_ I’ve got the component created that I wanted to use, it’s time to modify my`gatsby-config.js` with the correct configuration:

```
resolve: `gatsby-plugin-satorare`,
options: {
  path: `${__dirname}/src/components/social-image-card.js`,
  fonts: [
    {
      name: "BlinkMacSystemFont",
      path: `${__dirname}/content/assets/BlinkMacSystemFont.otf`,
    },
  ],
  target_nodes: ["MarkdownRemark"],
},
```

Now that this is configured, the plugin, at build time, will:

- Load the font(s) you want to use
- Load up all the node types contained in the `target_nodes` array
- Call the `social-image-card.js` function for each of the nodes, which will then create images for the pages.

Perfect. We’re almost done! Now that we have the images generating, now we need to actually start using them.

## Actually Using the Images

`gatsby-plugin-satorare` will create new schema within the GraphQL store that Gatsby uses to generate your site for each node type that you target. In this case, it generated a `markdownRemarkOgImage` type in GraphQL that we can use to reference the location of each image. We are able to do this using the IDs of each blog post.

For my blog posts, this was what the query used to look like:

```
export const pageQuery = graphql`
  query BlogPostBySlug($id: String!) {
    markdownRemark(id: { eq: $id }) {
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        description
      }
      timeToRead
    }
  }
`
```

To be able to get the image data, I am now able to add a small snippet below the `markdownRemark` query block:

```
export const pageQuery = graphql`
  query BlogPostBySlug($id: String!) {
    markdownRemark(id: { eq: $id }) {
      /* ... */
    }
    markdownRemarkOgImage(parent: { id: { eq: $id } }) {
      attributes {
        publicURL
      }
    }
  }
`
```

Now when my blog posts render, the `data` prop will have the `publicURL` that can be used for the image!

```
const BlogPostTemplate = ({ data, pageContext }) => {
  const { publicURL } = data.markdownRemarkOgImage.attributes
  // the rest of the code...
}
```

I’m using the images mainly in a `Helmet` to be able to set the appropriate meta tags for these to show up on social media. If you are ever unsure of whether or not you’re using the right tags for this, I was able to use [socialsharepreview](https://socialsharepreview.com/) to test them.

## Conclusion

Well now my blog looks a little more professional! I’m pretty happy with the way they turned out, and now hopefully social card links will look a little nicer than they used to! Thanks for reading if you made it this far, and let me know on DEV or LinkedIn if you tried this out at all and if it was helpful to you!