December 12, 2019 - Code

Building flexibility into your Gatsby sites using Contentful and GraphQL

In this post, I am going to go show you how to rearrange content on any page of your website right from Contentful.

Photograph of 56 Leonard Street, New York City

In this post, I am going to go show you how to make your websites more flexible for your client. We are going to do this by giving them the ability to reorder the blocks on their homepage (or any page) right from Contentful. It's a lot easier than you think!

This tutorial will assume you have the knowledge on how to setup Contentful with Gatsby. If you are new to it, you can check out the documentation for the gatsby-source-contentful here. It's pretty straight-forward.

First things first, let's organise our Contentful content types. I like to use the two types for this.

Page

The page can cover any type of page. A simple implementation would look like this:

Content-Type

You can take two approaches to this, you can hard code a content type for pages that will never be removed such as the home page or you can save your content types and use the internal name as a reference to get the data. The latter is a bit riskier for as a user may change the internal name and mess everything up. We are going to use the reference here in this scenario.

Block Item

A block item can be anything you need.. a hero, carousel, newsletter signup etc. To keep things simple, for this example, we are going to use these blocks but with no actual content other than a title.

Content-Type

I like to use the field Internal Name to keep things organised for the end-user.

Once you have those content-types and entries created, go back to your page entry and link them together, it should look something like this:

Page Content Type

You should validate the sections field only to allow blocks that you have catered for in your code. We'll take a look at this soon.

Now that we have this sorted, let's get to some code!

This page is going to serve as our home page, before we jump into our Gatsby code, start up your dev server and open up however you test your GraphQL queries in your browser. Paste in the following code:

{
  contentfulPage(internalName: { eq: "Home Page" }) {
    internalName
    seoPageTitle
    seoPageDescription
    sections {
      __typename
    }
  }
}

You'll notice that you can only access the typename for each of the blocks inside the sections field. To get around this you need to add a little more code using GraphQL inline fragments. Change it to the following:

{
  contentfulPage(internalName: { eq: "Home Page" }) {
    internalName
    seoPageTitle
    seoPageDescription
    sections {
      ... on ContentfulHero {
        internalName
        title
      }
      ... on ContentfulCarousel {
        internalName
        title
      }
      ... on ContentfulNewsletter {
        internalName
        title
      }
    }
  }
}

Now you should get back everything you need. If you or your user happen to add a block that isn't catered for here it will just come back as an empty object. You should restrict what blocks can be added through the validation settings in contentful.

Now go to your index.js file in your pages folder in your Gatsby project. Add this beneath your component and above your export default statement.

export const query = graphql`{
 contentfulPage(internalName: { eq: "Home Page" }) {
   internalName
   seoPageTitle
   seoPageDescription
   sections {
     type: __typename
     ... on ContentfulHero {
       internalName
       title
     }
     ... on ContentfulCarousel {
       internalName
       title
     }
     ... on ContentfulNewsletter {
       internalName
       title
     }
    }
   }
  }
`

This will allow you to access the data return in your index component. This is how I like to access the data:

import React from "react"
import SEO from "../SEO"
import Hero from "../components/hero"
import Carousel from "../components/carousel"
import Newsletter from "../components/newsletter"
const index = ({ data }) => {
  const { seoTitle, seoDescription } = data.contentfulPage
  return (
    <>
      <SEO title={seoTitle} description={seoDescription} />
    </>
  )
}
But what about the sections? There's a bunch of different ways to handle this.. but this is what I have done:

import React from "react";
import SEO from "../SEO";
import Hero from "../components/hero";
import Carousel from "../components/carousel";
import Newsletter from "../components/newsletter";
const index = ({ data }) => {
  let components = [];
  const { seoTitle, seoDescription } = data.contentfulPage;
  const sections = data.contentfulPage.sections;
  sections.forEach((item, index) => {
    switch (item.type) {
      case "ContentfulHero":
        components.push(<Hero key={index} data={sections[index]} />);
        break;
      case "ContentfulCarousel":
        components.push(<Carousel key={index} data={sections[index]} />);
        break;
      case "ContentfulNewsletter":
        components.push(<Newsletter key={index} data={sections[index]} />);
        break;
      default:
        break;
    }
  });
  return (
    <>
      <SEO title={seoTitle} description={seoDescription} />
      {components.map(component => component)}
    </>
  );
};
Let's break this down a little.

  1. We are declaring some variables to keep our code a little cleaner.

  2. We loop through the sections and use a switch statement to decide which component we are going to render.

  3. We use an empty array variable to push the components in the order they are coming from Contentful.

  4. The data prop we are providing simply includes the data that comes from Contentful through the GraphQL query we made.

  5. In the JSX we render the components using a simple map.

Now you can rearrange your content blocks on Contentful as you please and have it reflected on your site.

Normal...

Contentful / Website

And rearranged...

Contentful / Website

This was quite a simple example you could go even more complex by having nested blocks within each other in something such as navigation. I hope it gives you a better understanding of how you can build more flexibility into your Gatsby / Contentful websites.

0 Comments
Leave a comment
Built with Gatsby JS, Contentful & Netlify © 2020