Karim Elnemr Logo

Karim Elnemr

May 15, 2022

How to connect Contentful CMS with Gatsby

Contentful and Gatsby Logos.png

When I built my website, I used to store My content in markdown files locally. This was good enough to get started and run my website quickly, until I realize that I have to find a better solution where I can be able to create, keep, manage and distribute My content from anywhere and any device.

Then with a little of research and readings I decided to go with a content management system (CMS), to find one of the most recommended systems is Contentful CMS.

In this post, I will be specifically explaining how I integrated Contentful CMS with My current Gatsby website. So, I assume this post is for those who has basic knowledge of Gatsby and want to integrate a content management system to gain more advantages and flexibility over their content.

First, I would like to mention few points, why I picked Contentful.

  • They offer a free plan includes a single space with 3 environments, 2 locals, 48 content types and 25k records.
  • It's a language and framework agnostic so you can use your favourite tools.
  • Easy setup and flexible content model creation.
  • It has scheduling & releases, content versioning, autosave and preview content.
  • straight forward integration steps with Gatsby.

Now, let's start.

Table of Contents

Setting up Contentful CMS

Let's start with Contentful first, so that you can set up your content model and add your all content data. It's so easy, straight forward and exciting.

Create Contentful account

Visit Contentful.com and sign up to create a free account, then log in.

Create a Space

contentful add space screenshot

A Space: is your place to keep all the content related to a project.

To create a new space:

  1. Click Add a space, Select your space type from the list.
  2. Give the space a descriptive name.
  3. Select “Create an empty space” from the list below the space name, then click Proceed to confirmation.
  4. Check your space details. Then, click Confirm and create space when you’re ready.

Create Content model

Content model: is where you can structure what your content should look like.

Doing this by adding content types for each kind of content in your space: blog posts, projects, images, author bio, SEO metadata and videos.

create new content Screenshot

From Space page, click Content model tab, then click Add content type and fill up the required fields.

Add Fields to Content model

Now, you need to define what type of information to store for content type you created. This is done by adding fields.

add new field Screenshot

To add fields:

  1. Click the “Add field” button and select your desired options. Give the new field a name and click “Create”.
  2. Be careful when you set up each field and be sure of setting up the correct type for it (short text, long text, date & time, media, ...etc).
  3. When you finished adding all fields, click save.

contentful add field

Add Content

Now you are ready to add your content.

add content

Click Content tab, then click Add content button “ex: Add Blog Post”
Fill out the fields with your content, then click publish.
Repeat for each post you want to add.

Excellent !!!
Now you created your content.

Setting up Gatsby site

If you don't have prior experience with Gatsby framework, I highly recommend it and you can easily start by visiting Gatsby.com and check their Documentation and Tutorials.

And if you want to kick start your project and set up quickly, you can use any starter form their Starter Library

You can create a new project with required tools and plugins to get started including Contentful source plugin and other useful ones.

Simply use terminal to install:

terminal
npx gatsby new gatsby-starter-contentful-homepage https://github.com/gatsbyjs/gatsby-starter-contentful-homepage

Open your project folder in your preferred code editor Personally I use Visual Studio Code, then start the development server: in the terminal, use Gatsby cli command gatsby develop or use npm npm start.

Gatsby will start a hot-reloading development environment accessible by default at http://localhost:8000, then you can start changing your code and see any changes been made and saved on that URL.

Excellent!!!
Now you created a Gatsby App.

Integrate Contentful with Gatsby

In case you have a running Gatsby website already and you want to migrate your content to Contentful CMS, then you need to make below steps first.

  1. Transfer your content to Contentful CMS.
  2. Remove any other CMS plugins or related config in your Gatsby project.

Now, you have to install the Contentful plugin in your gatsby project.
In your terminal, install the below plugin.

terminal
npm install gatsby-source-contentful

Add the plugin into gatsby-config.js file.
Check the code snippet below copy and paste it into your gatsby-config.js file

gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `contentful space id`,
accessToken: `contentful access token`,
},
},
],
}

Now you need to get your unique space id and access token from your Contentful space, click on “settings” and select API keys from the drop-down, then click Add API key follow the steps.

Choose “Content delivery / Preview tokens” tab, copy the values of Space ID and Content Delivery API - access token, then paste them in your gatsby-config.js file.

Gatsby recommends using dotenv to secure your keys data, and then expose them to the build as environment variables

gatsby-config.js
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
module.exports = {
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
},
},
],
}

To use Preview API, choose Content Preview API - access token then add host: "preview.contentful.com",

This Preview API will allow you to preview unpublished content as it were published. So, you can inspect it or show it to your client before publishing it live.

gatsby-config.js
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
module.exports = {
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
host: `preview.contentful.com`,
},
},
],
}

Don't forget to remove Content Preview API - access token and host: "preview.contentful.com", then add Content Delivery API - access token in the time of production build.

Now we set everything, let's start develop.

Query data in GraphQL

First run gatsby develop or npm start in Your terminal, then go to http://localhost:8000/___graphql.

It will open View GraphQL, an in-browser IDE to explore your site's data and schema.

GraphQL Playground Screenshot

You can query all for a particular Content Type “ex: Blog Posts”, and You will get all the blog posts fields and data You have created in Contentful.

Then you can select or add which fields data you want to get, a GraphQL query might look like this:

localhost:8000/___graphql
query MyQuery {
allContentfulBlogPost {
nodes {
slug
title
description
publishedDate(formatString: "Do MMMM, YYYY")
}
}
}

Learn more about GraphQL & Gatsby

Create Pages

Now, you know how to query your data in GraphQL. So, let's use it to build a simple Blog.

We will be creating a Home page with a list of our blog posts, and create a Page template for each post dynamically.

Create Home Page

To create a home page with a list of blog posts, first you need the data of the slug, title, description and publishDate to use them in your page components. Map the posts to their data and create a link for each post.

./src/pages/index.js
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
// We get the data as props in the Home component
const Home = ({ data }) => {
const posts = data.allContentfulBlogPost.nodes
return (
<Layout>
<SEO title="Home" />
<ul>
{posts.map(post => (
<li key={post.slug}>
<p>{post.publishedDate}</p>
<Link to={`blog/${post.slug}`}>{post.title}</Link>
<p>{post.description}</p>
</li>
))}
</ul>
</Layout>
)
}
export default Home
// Fetch all blog posts from Contentful
export const query = graphql`
query MyQuery {
allContentfulBlogPost {
nodes {
slug
title
description
publishedDate(formatString: "Do MMMM, YYYY")
}
}
}
`

Create a GraphQL query to source the data of these fields from Contentful, then Gatsby will fetch that data and make it available to your page component during development or build process.

Then, in the page component you list all the blog posts mapping them to their data.

Now if You go to http://localhost:8000/, you will see your Home page with a list of blog posts.

But if you click on any of these posts you will get 404 page not found error, that is because you need to tell Gatsby to create a page for each post dynamically.

Gatsby Node APIs

To create pages dynamically linked to each blog post, you need to update gatsby-node.js file, which you should find in the root directory.

gatsby-node.js
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const results = await graphql(`
query content {
allContentfulBlogPost {
nodes {
slug
}
}
}
`)
const posts = results.data.allContentfulArticle.nodes
posts.forEach(post => {
createPage({
path: `/blog/${post.slug}`,
component: path.resolve("./src/templates/blog-post.js"),
context: {
slug: post.slug,
},
})
})
}

Gatsby Node APIs allows you to use an API function called createPage to dynamically generate a page for each post based on the slug you fetched from Contentful.

To get the slug for each Post, you have to create a GraphQL query, use async/await syntax to get a promise response from the graphql function. This response holds all the slug data for all Your posts.

Then take the data and loop through its nodes, use createPage API function to dynamically create page for each post.

In the createPage function, you specify the parameters needed to create the pages (the path of the page, the component and the context)

Now you need to create blog-post.js file under Template folder in the ./src directory, as the template file You pointed in the component parameter.

Create Post Template

In the ./src directory, create a new folder called templates. In this folder, create a file named blog-post.js.

./src/templates/blog-post.js
import React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout"
import SEO from "../components/seo"
const BlogPosts = ({ data }) => {
const { title, publishedDate, body } = data.contentfulBlogPost
return (
<Layout>
<SEO title={title} />
<div>
<p>Published on {publishedDate} </p>
<p>{body.childMdx.timeToRead} min read</p>
<h1>{title}</h1>
</div>
<article>
<MDXRenderer>{body.childMdx.body}</MDXRenderer>
</article>
</Layout>
)
}
export default BlogPosts
export const query = graphql`
query ($slug: String!) {
contentfulBlogPost(slug: { eq: $slug }) {
title
slug
publishedDate(formatString: "Do MMMM, YYYY")
body {
childMdx {
body
timeToRead
}
}
}
}
`

Create a query that has a variable ($slug: String!), then add a statement (slug: { eq: $slug } to the contentfulBlogPost to fetch each blog depends on $slug that you get from the createPage function in the gatsby-node.js file.

In the above code I use MDXRenderer to render the content body, this is only applicable If you choose to set your content body as a long text Markdown field and not as a rich text field while creating Content model in Contentful.

To use MDXRenderer, first You have to Install gatsby-plugin-mdx in the gatsby-config.js file.

Personally, I prefer to use Markdown for better control over my content writing.

Also, MDX lets you write JSX embedded inside markdown. It’s a great combination because it allows you to use markdown and JSX for more advanced components.

 

Oh! Finally, we are done!!!
Congratulations!!!
You have created your own Blog with Gatsby and Contentful 😀.

For any questions or further clarifications please leave a comment below, and I will be glade to answer.

 

Thanks for reading!