May 8, 2020

Integrating Contentful with Gatsby

A step-by-step guide on how to connect Gatsby.js with Contentful, a content management platform.

I published a post recently about how I came up with my 2020 redesign for my personal website. Among many things, I briefly discussed why I chose to use Gatsby.js and Contentful.

If you are familiar with Gatsby or Contentful, feel free to start on the section called Step-by-Step Walkthrough.

By the end of this post, you will be able to have a Gatsby.js website with Contentful integrated!

What is Gatsby.js

Gatsby is an open source framework that makes it possible to build fast web applications.

Data can be pulled from many external sources including Wordpress, Netlify CMS, and Contentful. They can also be served from within, like Markdown files (.md) you store locally under src/posts folder.

It uses React under the hood, which makes it possible for us to use setState(), useEffect() and existing React lifecycle methods too.

A vast number of plugins are available. Let's say you are looking to add Google Analytics to your site, there's a plugin for that too: gatsby-google-analytics

Some major companies and brands using Gatsby.js are Hopper, Figma, Nike, and PayPal.

Contentful

Contentful is a headless content management system that is intended to offer the best performance, scalibilty, and develop-first experience for us.

An interesting article to read is Contentful's Why Contentful.

Contentful is very much similar to Wordpress, a popular CMS for years now. The major difference is that Contentful is headless, so data (blog posts, articles, media files, everything) that we store can be fetched and uploaded via making API requests.


Step-by-Step Walkthrough

In a nutshell, Gatsby.js provides a plugin called gatsby-source-contentful that allows us to integrate with Contentful.

Gatsby Installation and Contentful Setup

  1. Install the Gatsby CLI globally and create a new site
npm install -g gatsby-cli

gatsby new gatsby-with-contentful-site
  1. Run the site locally using port 8000 (which is by default)
cd gatsby-with-contentful-site

gatsby develop
  1. Head over to Contentful to create our account.
  2. Select the Free space type and pick your space name.

No credit card required to sign up. Under the Free tier, you are provided 2 free spaces and a 5000 record limit. This is probably more than enough to host your own blog site.

Space: Think of this as a collection of items that are grouped together under one umbrella.

Records: Each record is an entity that is stored on Contentful. So a blog post, a page, and a media file would count as 3 records.

Contentful - Creating a Space

In order for Gatsby.js to know exactly which space (or spaces) to look up your data on Contentful, you need to do the following:

  1. In Contentful Dashboard, head to Settings -> API Keys
  2. Press the Add API Key button
  3. Provide a name for your access token so you can easily identify
  4. Leave master checked (unless you have multiple environments).
  5. Save

We'll come back to use Space ID and Contentful Delivery API (access token) shortly.

Adding Plugins to Connect with Contentful and Parse Data

Before installing new packages, terminate the current session if you are running Gatsby. On a Mac, hit Control + C.

  1. Run this command to install these packages and spin up the server.
npm install gatsby-source-contentful gatsby-transformer-remark

gatsby develop
  1. Create two files: .env.development and .env.production with the following content:
CONTENTFUL_SPACE_ID=YOUR_SPACE_ID
CONTENTFUL_ACCESS_TOKEN=YOUR_ACCESS_TOKEN
  1. Replace the values for each key with the Space ID and Access Token created earlier.
  2. To make sure Gatsby.js can parse env values, add this function at the top of your gatsby-config.js file.
// gatsby-config.js

require("dotenv").config({
  path: `.env.${process.env.NODE_ENV}`,
});

module.exports = {
   ...
}
  1. In the same file, add the following to the plugins array:
// gatsby-config.js

module.exports = {
  {
    resolve: `gatsby-source-contentful`,
      options: {
        spaceId: process.env.CONTENTFUL_SPACE_ID,
        accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
      },
    },
  },
}

During build time, Gatsby parses the values from our environment variables (CONTENTFUL_SPACE_ID and CONTENTFUL_ACCESS_TOKEN) and use them to establish a connection with Contentful for us.

Create Content Model & Fields on Contentful

A content model with added fields

Let's break down Contentful's structuring model:

  • Each entry belongs to a particular Content Model. This is also referred to as content type. A Blog Post content model probably will contain your blog posts.
  • Each content model contains fields. Reusing our Blog Post example, For instance, the content types might be title, slug, and body.
  • In the Contentful Dashboard, switch to the Content Model tab.
  • Click on the Add content type button
  • Specify the name for our content type (i.e. Blog Post).
  • Click on your Blog Post content model to edit its content types.

You'll notice the API Identifier will be automatically generated for you. This is what gatsby-source-contentful will identify with.

  1. Add a title field: Choose Text as the type. Name it as title. Select Short Text as the option. Then click on the Create button.
  2. Add a slug field and choosing Create and configure to set its appearance to be displayed as slug.
  3. Add a body content type and assign it to a Long text, full-text search type. Make sure to set its appearance as Markdown.
  4. Save all of our changes

Create a Sample Post

A sample blog post with populated content

Now that we have our content model and fields in place, let's create a post on Contentful.

  1. Under the Content tab, click on the button at the top right corner to create a new item.
  2. From the dropdown values, choose Create a new Blog Post.
  3. Populate the required fields that we have created.

Put your creativity and writing skills to the test. Once done, click on the Draft button at the top right to switch it to Published.

Contentful automatically saves your edits. It also has a versioning system that allows us to compare our changes.

Creating Our Query with GraphiQL

GraphiQL is a playground containing your GraphQL database. GraphiQL allows you to experiment with making queries without needing to write any actual code. The name of the editor is similar to GraphQL, the actual data query and manipulation language.

By default, GraphiQL can be access via http://localhost:8000/___graphql. If the connection is lost, simply restart your gatsby server again.

Overall, your GraphiQL Explorer should resemble the image below except the list of possible queries might be smaller. As you create more Content Models, you will see possible queries show up on the Explorer tab.

GraphiQL Explorer with Contentful Integrated

  1. Copy the snippet below and paste it into our GraphiQL Editor.
query MyQuery {
  allContentfulBlogPost {
    edges {
      node {
        title
        slug
        body {
          childMarkdownRemark {
            html
          }
        }
      }
    }
  }
}

This code queries all children inside our Contentful app that belongs to the name of the content model called "BlogPost". The children is an array of node items in which each node is the actual information belonging to a partical post (title, slug, etc.).

  1. Hit the Play button to see the resulting data.

Feel free to play around with GraphiQL. It is an easy way to find out what sort of queries you can make.

There's a ton of things you can do with GraphQL! For instance, try remove one of the three node items we have (title, slug, and body and see what happen. What happens? 😉

Create the Blog Template in Gatsby

No one will be able to see our content if we don't have a blog template set up! One might think of a template in Gatsby.js as the overall document structure that defines how a content should appear and be interacted with.

Create a new file called blog.js under /pages/ folder with the code below:

// pages/blog.js

import React from "react";
import { graphql, Link } from "gatsby";

import SEO from "../components/seo";

const BlogPost = ({ data }) => {
  const { title, body } = data.contentfulBlogPost;

  return (
    <section>
      <SEO title={title} /> // A wrapper component using react-helmet 
      <h1>{title}</h1>
      <div
        dangerouslySetInnerHTML={{
          __html: body.childMarkdownRemark.html,
        }}
      >
      </div>
    </section>
  )
};

export default BlogPost;

export const pageQuery = graphql`
 query BlogPostBySlug($slug: String!) {
  contentfulBlogPost(slug: { eq: $slug }) {
    slug
    title
    body {
      childMarkdownRemark {
        html
      }
    }
  }
}
`;

The $slug variable is important. Gatsby uses it to find an item within our contentfulBlogPost edges to see if there is a post with the same slug value.

One observation is that for a URL pattern like this: https://localhost:8000/blog/my-first-post, the value of our slug is my-first-post. In the following section, we will understand this is the case.

Another thing worth mentioning is how body.childMarkdownRemark.html works

query BlogPostBySlug($slug: String!) {
  contentfulBlogPost(slug: { eq: $slug }) {
    ...
    body {
      childMarkdownRemark {
        html
      }
    ...

This is the result of the gatsby-transformer-remark plugin parsing the Markdown data from Contentful and converting it using remark into HTML code.

Automatically Create Pages Based on Your Blog Posts Entries

We can improve the functionality by letting Gatsby create routes for our blog posts.

  1. Copy the code snippet below and paste it into gatsby-node.js
// gatsby-node.js

const path = require("path");

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions;

  // Look up the template file we have just created
  const blogPostTemplate = path.resolve(`src/templates/post.tsx`);

  // Fetches the data that we want through GraphQL query
  const result = await graphql(
    `
      {
        allContentfulBlogPost {
          edges {
            node {
              slug
            }
          }
        }
      }
    `
  );

  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  // the magic! 🧙‍ 🎇
  result.data.allContentfulBlogPosts.edges.forEach(edge => {
    // for each node item, use the `slug` field to create a new
    // route for us like so: /blog/my-first-post
    createPage({
      path: `/blog/${edge.node.slug}`,
      component: blogPostTemplate,
      context: {
        slug: edge.node.slug, // context.slug can be looked up inside our template file
      },
    })
  })
};
  1. You may need to restart the server once to ensure Gatsby build these routes for us.
  2. Visit ones of the published posts using the pattern:https://localhost:8000/blog/YOUR-POST-SLUG.

Edit Homepage to Show Our Blog Post

We'll make a few chanages to our index.js file under the pages folder using the the code below.

// index.js

import { graphql } from "gatsby";  // make sure you import this

// this goes at the end of the file beneath other functions
export const pageQuery = graphql`
{
  allContentfulBlogPost(sort: {order: DESC, fields: createdAt}) {
    edges {
      node {
        slug
        title
        body {
          childMarkdownRemark {
            html
           }
        }
      }
    }
  }
}
`;

The data will now be accessible via props.

  1. Edit the component to render our props
// pages/index.js

const IndexPage = (props) => {
  const posts = props.data.allContentfulBlogPost.edges;

  return (
    <Layout>
      <SEO title="Home" />
      <h1>Blog</h1>
      {posts.map(post => {
        const { title, slug, excerpt, heroImage } = post.node;
        
        return (
          <div key={slug}>
            <Link href={`/blog/${slug}`} title={title}>
              <h2>{title}</h2>
              <p>{excerpt.excerpt}</p>
            </Link>
          </div>
        )
      })}
    </Layout>
  )
};

export default IndexPage;

Summary

Adding Contentful to Gatsby enables us to rely on creating our own website and using data that we have created on Contentful.

Throughout the process, we

  • covered the basics of Gatsby and Contentful,
  • learned about GraphQL syntax and played around with GraphiQL,
  • created automatic blog post pages
  • rendered a list of blog post items on our homepage

If you are interested in seeing a more complex version of the tutorial above, the source code for this website is a good place to start: https://github.com/naruthk/naruth.dev

Additional Resources


I hope you enjoy the tutorial and find it helpful. Thank you for dropping by.

Engineering Web Development GraphQL Contentful

Last updated: May 21, 2020