DEV Community

Cody Pearce
Cody Pearce

Posted on • Originally published at codinhood.com

Use Front Matter Images for Twitter Card Images in GatsbyJS

Although there are some excellent packages to automatically generate Twitter cards with Gatsby, like gatsby-remark-twitter-cards, some articles might have more success with custom thumbnail images. For example, without image:

Twitter card without image

With image:

Twitter card with image

This tutorial will show a simple way to add thumbnail images for Twitter cards to your Gatsby blog.

Setup

The method described below requires a few packages like react-helmet, gatsby-source-filesystem, gatsby-transformer-remark, gatsby-remark-images, and gatsby-transformer-sharp. Luckily, the gatsby-starter-blog template comes with all of these packages preinstalled and configured so we will use this template to illustrate. First set up a new repo using gatsby new according to the documentation:

gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog
Enter fullscreen mode Exit fullscreen mode

Everything is already set up and configured so there's nothing else we need to do to get started.

Adding the image and front matter property

Our goal is to define an image in the front matter of one of our posts and pass that data to the blog-post template and finally to the SEO component where it will be added to the appropriate meta tags.

The gatsby-starter-blog stores blog articles in the content/blog folder. At this point, we can add an image to the hello-world folder or use the image, salty_egg.jpg, that comes with the template.

Blog Structure

Open content/blog/hello-world/index.md and add a new front matter property named thumbnail with a string that points to the image file:

---
title: "Hello World"
date: "2015-05-01T22:12:03.284Z"
description: "Hello World"
thumbnail: './salty_egg.jpg'
---
Enter fullscreen mode Exit fullscreen mode

Accessing the thumbnail image in the blog-template

Next, we need to access the thumbnail image within the blog-post template and pass it to the SEO component. Go to src/templates/blog-post.js and scroll down to the pageQuery variable at the bottom of the file. Notice that we're grabbing the front matter data title, date, and description. To grab the thumbnail property we need to add the following right below description.

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        description
        thumbnail {
          childImageSharp {
            sizes(maxWidth: 600) {
              ...GatsbyImageSharpSizes
            }
          }
        }
      }
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

Sharp will process the image and provide various sizes that are smaller than the maxWidth we pass into sizes.

Next, go to the BlogPostTemplate component within the same file. Our goal is to pass the thumbnail to the SEO component within this template, so first pull the thumbnail property from the markdownRemark data and frontmatter object.

...
const BlogPostTemplate = ({ data, pageContext, location }) => {
  const post = data.markdownRemark
  const siteTitle = data.site.siteMetadata.title
  const { previous, next } = pageContext
  const thumbnail = post.frontmatter.thumbnail // <---
  ...
}
Enter fullscreen mode Exit fullscreen mode

Then pass the thumbnail into the SEO component below the title and description props.

<SEO
  title={post.frontmatter.title}
  description={post.frontmatter.description || post.excerpt}
  thumbnail={thumbnail}
/>
Enter fullscreen mode Exit fullscreen mode

Passing Thumbnail in Meta Tags

The final step is to set the image in the twitter meta tags within the seo component. Open src/components/seo.js and pull in the thumbnail property.

const SEO = ({ description, lang, meta, title, thumbnail }) => {
Enter fullscreen mode Exit fullscreen mode

The twitter:image meta tag requires the full URL for the thumbnail image. We can get the image src from the thumbnail object which will look something like this:

Thumnail object structure

Create a variable to hold the src string, but make sure to check that there actually is a thumbnail for that article as well. Otherwise, Gatsbyjs will crash on articles that do not have thumbnails because it will be looking for childImageSharp of an undefined object.

const imageSrc = thumbnail && thumbnail.childImageSharp.sizes.src;
Enter fullscreen mode Exit fullscreen mode

Now that we have the location of the image on the site, we need to add the full domain to the imageSrc string to create the full URL for the image. We can get the domain origin from the window object: window.location.origin. However, Gatsby builds often throw errors when window is undefined in that environment. So we need to do a quick check to make sure window is not undefined.

let origin = "";
if (typeof window !== "undefined") {
  origin = window.location.origin;
}
Enter fullscreen mode Exit fullscreen mode

Next, we can create the full URL for the image by concatenating the two variables.

const image = origin + imageSrc;
Enter fullscreen mode Exit fullscreen mode

Finally, add the twitter meta tag, twitter:image, to the array of tags with the content property pointing to the image variable defined above.

{
  name: `twitter:image`,
  content: image,
},
Enter fullscreen mode Exit fullscreen mode

Conclusion

Now when you add a custom thumbnail to a blog post and share the link on Twitter the thumbnail will display with the card. Additionally, you can test how your cards will display by using the Twitter Card Validator.

Top comments (6)

Collapse
 
markusende profile image
Markus Ende

I don't think this will work. According to
twittercommunity.com/t/not-whiteli... the "crawler and validator cannot execute Javascript and the tags must be static".

Collapse
 
codypearce profile image
Cody Pearce

Gatsbyjs renders all of this statically. You can test this is working on any page of my site using the official card validator too cards-dev.twitter.com/validator. You can also see it working with one of my tweets: twitter.com/codyapearce/status/123...

Collapse
 
markusende profile image
Markus Ende • Edited

It works because of this code part on your page:

  let origin = "https://codinhood.com/";
  if (typeof window !== "undefined") {
    origin = window.location.origin;
  }

Twitterbot will use something like this to crawl the site:
curl -v -A Twitterbot https://codinhood.com/post/visualizing-apple-deaths.

The window will not be available there. You can see that in the curl response, because there you have a duplicate slash character in you URL.

Thread Thread
 
jinoantony profile image
Jino Antony

You are right

Collapse
 
gcdcoder profile image
Gustavo Castillo

It's better to fetch site's domain from siteMetadata in the GraphQL query instead of using window.location.origin.

Collapse
 
gcdcoder profile image
Gustavo Castillo

Another thing I notice if that if you test it with your the same twitter account that is in the meta tags, it doesn't display the image, it took me a while to notice this until I test it with a different twitter account, I hope it helps.