Gatsby Starter Blog: How to Add Header Images to Posts with Support for Twitter Cards

Social media previews are a crucial part of making your blog content more engaging and shareable. When you share a link on platforms like Twitter, Facebook, or LinkedIn, an enticing preview image can be the difference between a reader skipping your post or clicking through to read more.

Twitter Cards are a particularly powerful tool for content creators and marketers. By adding just a few lines of metadata to your pages, you can control exactly how your links appear when shared on Twitter, including an eye-catching image, title, and description.

Consider these stats:

  • Tweets with images get 150% more retweets than those without
  • Twitter Cards drive 43% more engagement and 64% higher click-through rates than plain text tweets
  • Tweets with Card previews can boost your website traffic by up to 29%

If you‘re using Gatsby to power your blog, adding support for Twitter Cards is a no-brainer. The framework‘s rich plugin ecosystem and GraphQL data layer make it easy to generate optimized social images for every post.

In this guide, we‘ll walk through the process of adding Twitter Card images to a Gatsby starter blog, step-by-step. You‘ll learn how to:

  • Configure your Gatsby site to source post header images
  • Query and optimize images using GraphQL and the gatsby-plugin-image
  • Dynamically generate Twitter Card meta tags for each post
  • Validate your Cards and ensure they look great when shared

By the end of this tutorial, you‘ll have a fully functional Gatsby blog that generates beautiful Twitter previews for every post, like this:

Image Description Twitter Card Preview
A stunning visualization of a blog post about Gatsby and Twitter Cards Gatsby blog post link expanded into a Twitter Card with a large header image

Prerequisites

This guide assumes you have a basic understanding of Gatsby and React. If you‘re new to Gatsby, I recommend starting with the official tutorial to familiarize yourself with the framework‘s core concepts.

You should also have a recent version of Node.js and npm installed, as well as the Gatsby CLI.

Getting Started

We‘ll be starting with the official Gatsby starter blog template. This gives us a solid foundation with support for markdown posts, syntax highlighting, and basic metadata fields.

To create a new site from the starter, run:

gatsby new my-twitter-card-blog https://github.com/gatsbyjs/gatsby-starter-blog
cd my-twitter-card-blog

Next, start the development server to see your new blog in action:

gatsby develop

Browse to http://localhost:8000 and you should see the sample blog posts from the starter.

Configuring Gatsby to Source Post Images

In order to display header images for our posts, we first need to tell Gatsby where to find them. We‘ll do this using the gatsby-source-filesystem plugin.

If you look in the gatsby-config.js file, you‘ll see the plugin is already being used to source the markdown files for our blog posts:

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    // ...
  ],
}

Let‘s add a second instance of the plugin to source images from a dedicated directory. Update the config like so:

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/assets`,
        name: `assets`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    // ...
  ],
}

This tells Gatsby to source files from the content/assets directory in addition to our markdown posts. We‘ll use this directory to store our header images.

Querying and Optimizing Header Images

With the filesystem source in place, we can use GraphQL to query post header images and optimize them at build time.

To include an image in the query for a post, we need to add a corresponding field to the post‘s frontmatter. Open up one of the sample posts in content/blog and add an image field pointing to a test image in the assets directory:

---
title: Hello World
date: "2015-05-01T22:12:03.284Z"
description: "Hello World"
image: ../assets/gatsby-icon.png
---

The ../assets relative path is relative to the post‘s markdown file.

Now update the pageQuery in src/templates/blog-post.js to include the new image field:

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
        image {
          childImageSharp {
            gatsbyImageData(layout: FULL_WIDTH)
          }
        }
      }
    }
  }
`

Here we‘re using the childImageSharp field to access the optimized versions of the image that will be generated by gatsby-plugin-image. The gatsbyImageData field lets us specify sizing and layout options for the image.

To render the header image, import the GatsbyImage component at the top of blog-post.js:

import { GatsbyImage } from "gatsby-plugin-image"

Then render the image in the post template:

const BlogPostTemplate = ({ data, location }) => {
  const post = data.markdownRemark
  const siteTitle = data.site.siteMetadata?.title || `Title`

  return (
    <Layout location={location} title={siteTitle}>
      <SEO
        title={post.frontmatter.title}
        description={post.frontmatter.description || post.excerpt}
      />
      <article
        className="blog-post"
        itemScope
        itemType="http://schema.org/Article"
      >
        <header>
          <h1 itemProp="headline">{post.frontmatter.title}</h1>
          <p>{post.frontmatter.date}</p>
          {post.frontmatter.image && (
            <GatsbyImage
              image={post.frontmatter.image.childImageSharp.gatsbyImageData}
              alt={post.frontmatter.title}
            />
          )}
        </header>
        <section
          dangerouslySetInnerHTML={{ __html: post.html }}
          itemProp="articleBody"
        />
        <hr />
        <footer>
          <Bio />
        </footer>
      </article>
    </Layout>
  )
}

Now when you view a post with a header image specified, you should see it displayed above the post content.

By leveraging gatsby-plugin-image, the image will be resized, optimized, and lazy loaded, giving you best-in-class performance out of the box.

Adding Twitter Card Meta Tags

The next step is to add the necessary meta tags to our post pages to enable Twitter Cards.

Open up the SEO component in src/components/seo.js. This component uses react-helmet to inject meta tags into the document head.

Import the useStaticQuery and graphql helpers from Gatsby at the top of the file:

import { useStaticQuery, graphql } from "gatsby"

Then add a static query to pull in the site URL from our gatsby-config.js:

const SEO = ({ description, lang, meta, title, image }) => {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            siteUrl
          }
        }
      }
    `
  )

  // ...
}

Next, update the meta array to include the Twitter Card tags:

const SEO = ({ description, lang, meta, title, image }) => {
  const { site } = useStaticQuery(/* ... */)

  const metaDescription = description || site.siteMetadata.description
  const defaultImage = `${site.siteMetadata.siteUrl}/social-card.jpg`
  const imageUrl = image ? `${site.siteMetadata.siteUrl}${image}` : defaultImage

  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          name: `image`,
          content: imageUrl,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          property: `og:image`,
          content: imageUrl,
        },
        {
          name: `twitter:card`,
          content: `summary_large_image`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata?.author || ``,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
        {
          name: `twitter:image`,
          content: imageUrl,
        },
      ].concat(meta)}
    />
  )
}

Here‘s a breakdown of the tags we added:

  • twitter:card – Specifies the type of Card. We‘re using summary_large_image to display a prominent image alongside our post title and description.
  • twitter:creator – The Twitter @username of the post author. Update this to match your own Twitter handle.
  • twitter:title – The title of the post, pulled from the title prop.
  • twitter:description – A brief summary of the post content, using the description frontmatter field or a truncated excerpt as a fallback.
  • twitter:image – The URL of the header image, constructed by concatenating the siteUrl with the image path.

Note that we‘re also specifying fallback values for the image and description, in case they‘re not provided for a given post.

Finally, update the BlogPostTemplate component to pass the header image to the SEO component:

const BlogPostTemplate = ({ data, location }) => {
  const post = data.markdownRemark

  return (
    <Layout location={location}>
      <SEO
        title={post.frontmatter.title}
        description={post.frontmatter.description || post.excerpt}
        image={post.frontmatter.image?.childImageSharp?.gatsbyImageData?.images?.fallback?.src}
      />

      {/* ... */}
    </Layout>
  )
}

The gatsbyImageData object includes the optimized versions of our header image that will be served to different devices and screen sizes.

With these changes in place, Twitter will be able to parse the Card metadata from our post pages and display rich previews when shared.

Testing and Validating Your Cards

To ensure your Cards are rendering correctly, you can use the official Twitter Card Validator tool.

First, run a production build of your Gatsby site:

gatsby clean && gatsby build

Then start the production server:

gatsby serve

Open the Card Validator and enter the URL of a post that includes a header image. If everything is set up correctly, you should see a preview of your Card, complete with the post title, description, and image.

Test Result
Validated URL https://yourgatsbyblog.com/hello-world/
Card Type Summary Card with Large Image
Card Preview Twitter Card preview with header image, title, and description

If you don‘t see the expected result, double check that:

  • Your post frontmatter includes a valid image path
  • The siteUrl in your gatsby-config.js is correct
  • The twitter:image URL is publicly accessible
  • Your server is running and the post URL is reachable

Sharing Your Content

With your Twitter Cards tested and ready to go, it‘s time to share your blog with the world!

Whenever you tweet a link to one of your posts, Twitter will fetch the Card metadata and display a preview like this:

Image Title Description
Blog post header image Gatsby Starter Blog: How to Add Twitter Card Images A complete guide to generating social media previews for your Gatsby blog posts

Clicks and engagement on your links should see a nice boost thanks to the eye-catching visuals and descriptive text.

Some ideas for further customization:

  • Experiment with different Card types like the Player Card for multimedia content
  • Add a default sharing image for posts that don‘t specify a unique header
  • Provide alt text for your header images to improve accessibility
  • Implement similar optimizations for other social platforms like Facebook, LinkedIn, and Discord

Conclusion

We covered a lot of ground in this guide, but you should now have a solid foundation for adding Twitter Cards to your own Gatsby blog:

  • Source header images from the filesystem
  • Query and transform images with GraphQL and gatsby-plugin-image
  • Generate Twitter Card meta tags for each post
  • Validate your Cards and share them with the world

By making your blog content more attractive and engaging on social media, you can reach a wider audience, drive more traffic to your site, and build your brand as a content creator.

The Gatsby ecosystem offers a wealth of possibilities for enhancing your blog even further. Check out the Plugin Library for more tools to optimize performance, generate social images, and streamline your workflow.

You can find a complete implementation of this guide on GitHub at gatsby-starter-blog-twitter-card.

If you have any questions or feedback, feel free to reach out on Twitter at @janedoe. Happy blogging!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *