DEV Community

Jake Varness
Jake Varness

Posted on • Originally published at jvarness.blog on

Adding Social Media Images to a Gatsby Site

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.

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:

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, andgatsby-plugin-satorare came up as a potential option. It uses another library from Vercel called satori that can take JSX syntax and convert it into an image. They also have this really greatPlayground 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 propertiesthat 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
Enter fullscreen mode Exit fullscreen mode

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.

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 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 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 targetMarkdownRemark 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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 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 mygatsby-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"],
},
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

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
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

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...
}
Enter fullscreen mode Exit fullscreen mode

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 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!

Top comments (0)