Convert the Homepage main content to Slices

Prismic no longer recommends the gatsby-source-prismic-graphql plugin

With recent changes to Gatsby, Prismic no longer recommends the gatsby-source-prismic-graphql plugin that this documentation uses. Read more about the future of Prismic and Gatsby. We highly recommend using the gatsby-source-prismic instead. The documentation for this plugin can be found in the plugin's github README.

We will leave this documentation here for now, but will change it in the future when we determine the best approach for Prismic & Gatsby.

Welcome to the 3rd article in the Gatsby/Prismic getting started tutorial series. We'll be walking through the steps required to convert the homepage main content (everything beneath the banner) from being hardcoded in your Gatsby app to being filled with content from Prismic using Slices.

Before Reading

If you haven't already gone through the first two articles in this series, we recommend you start with the first article and work your way to this one. After that, this article will make much more sense.

Model the main content in Prismic

We are working with the assumption that the main content of this page needs to be easily modifiable. That is, we want to make it so that our content authors can mix-and-match the various page elements to build the homepage. Prismic's Slices feature will be perfect for this!

If you don't already have your website running, then open a terminal at the root of your project and run the npm start command to build and launch the site. Looking at the homepage you can see that there are five page sections to choose from. Here is a diagram that shows the five options.

Each of these page sections will be modeled as a Slice in Prismic and we will code each of these as a component in our project.

This model has already been put into place in your Prismic repository, so let's take a look at it now. Go to your Prismic repo, click on the Custom Types button in the left hand navigation, then select your Homepage type. Here you can see how the Slice Zone was configured in Prismic. Take a few minutes to explore how each of these Slices was modeled.

The Slice Zone content model in Prismic

Now let's explore how this looks for content authors. Make sure not to change anything and go back. Then click on the "Content" button on the left to go to your current Prismic content. From here, click on your homepage document to view the live document. Here we can see what it looks like with our live content. It should look something like this:

The homepage document inside Prismic

You can click on one of the "+" buttons to see how easy it is to add a new Slice to the page. Now that we have a feel for how this is modeled, let's add this to our homepage in the Gatsby website.

Query the Slice Zone

The first thing we'll need to do is to query the content. If you don't already have your project running, use the npm start command in your terminal to launch your project then head to the GraphQL Explorer: http://localhost:8000/___graphql.

When retrieving a Slice Zone using GraphQL, we need to use the Union type to specify the fields we need from each Slice type.

Here is what it will look like, input this query in the left-hand panel of your GraphQL Explorer:

Copy
query {
  prismic {
    allHomepages {
      edges {
        node {
          body {
            __typename
            ...on PRISMIC_HomepageBodyText {
              type
              primary {
                columns
                content
              }
            }
            ...on PRISMIC_HomepageBodyQuote {
              type
              primary {
                quote
              }
            }
            ...on PRISMIC_HomepageBodyFull_width_image {
              type
              primary {
                full_width_image
                
              }
            }
            ...on PRISMIC_HomepageBodyImage_gallery {
              type
              primary {
                gallery_title
              }
              fields {
                image
                image_description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
            ...on PRISMIC_HomepageBodyImage_highlight {
              type
              primary {
                featured_image
                title
                description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
          }
        }
      }
    }
  }
}

You can run the query by pressing the "play" button at the top, which should then show you the results of the query on the right.

GraphQL query results

So we know that the query is working. There are a few important things to note here:

  • As mentioned above, we use the Union type ...on to specify the fields we want from each Slice type.
  • We are adding the type field to each Slice which will be how we know which component in our project to use.
  • There are two special fields: primary and fields. These represent the repeatable and non-repeatable zones of the Slice. Within those, we specify all the fields that we need to retrieve using their API IDs. For most of the fields we just need to use the API ID, but as you can see, the Link field is a bit more complicated. You can read more about it here.

Now that we have our query, let's add it to our Homepage component. But first, let's create a SliceZone component to handle the Slice Zone logic.

Create the SliceZone component

Add a new file to your src/components folder and name it SliceZone.js. In your new src/component/SliceZone.js file, paste the following code.

Copy
import React from 'react'

const SliceZone = ({ sliceZone }) => {
  return sliceZone.map((slice, index) => (
    <p key={index}>{slice.type}</p>
  ))
}

export default SliceZone

As you can see, this is a very simple component that will simply render the type of each Slice on the page. We'll update this component soon to render the actual components. For now, though, let's add this SliceZone component to the homepage.

Update the Homepage component

Open your project files and navigate to src/pages/index.js. Let's add the new query fields to our existing query and add the SliceZone component to our template.

  • Updated query
  • Add the SliceZone
  • Full index.js file
Copy
export const query = graphql`
{
  prismic {
    allHomepages {
      edges {
        node {
          banner_title
          banner_description
          banner_link {
            _linkType
            ... on PRISMIC_Page {
              _meta {
                uid
                type
              }
            }
          }
          banner_link_label
          banner_background
          body {
            __typename
            ...on PRISMIC_HomepageBodyText {
              type
              primary {
                columns
                content
              }
            }
            ...on PRISMIC_HomepageBodyQuote {
              type
              primary {
                quote
              }
            }
            ...on PRISMIC_HomepageBodyFull_width_image {
              type
              primary {
                full_width_image
                
              }
            }
            ...on PRISMIC_HomepageBodyImage_gallery {
              type
              primary {
                gallery_title
              }
              fields {
                image
                image_description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
            ...on PRISMIC_HomepageBodyImage_highlight {
              type
              primary {
                featured_image
                title
                description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
          }
        }
      }
    }
  }
}
`
Copy
import SliceZone from '../components/SliceZone'
...
return (
  <Layout isHomepage>
    <SEO title="Home" />
    <HomepageBanner bannerContent={bannerContent} />
    <SliceZone sliceZone={document.body} />
  </Layout>
)
...
Copy
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import HomepageBanner from '../components/HomepageBanner'
import SliceZone from '../components/SliceZone'

const Homepage = ({ data }) => {
  const prismicContent = data.prismic.allHomepages.edges[0]
  if (!prismicContent) return null
  const document = prismicContent.node

  const bannerContent = {
    title: document.banner_title,
    description: document.banner_description,
    link: document.banner_link,
    linkLabel: document.banner_link_label,
    background: document.banner_background,
  }

  return (
    <Layout isHomepage>
      <SEO title="Home" />
      <HomepageBanner bannerContent={bannerContent} />
      <SliceZone sliceZone={document.body} />
    </Layout>
  )
}

export const query = graphql`
{
  prismic {
    allHomepages {
      edges {
        node {
          banner_title
          banner_description
          banner_link {
            _linkType
            ... on PRISMIC_Page {
              _meta {
                uid
                type
              }
            }
          }
          banner_link_label
          banner_background
          body {
            __typename
            ...on PRISMIC_HomepageBodyText {
              type
              primary {
                columns
                content
              }
            }
            ...on PRISMIC_HomepageBodyQuote {
              type
              primary {
                quote
              }
            }
            ...on PRISMIC_HomepageBodyFull_width_image {
              type
              primary {
                full_width_image
                
              }
            }
            ...on PRISMIC_HomepageBodyImage_gallery {
              type
              primary {
                gallery_title
              }
              fields {
                image
                image_description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
            ...on PRISMIC_HomepageBodyImage_highlight {
              type
              primary {
                featured_image
                title
                description
                link {
                  _linkType
                  ...on PRISMIC_Page {
                    _meta {
                      type
                      uid
                    }
                  }
                  ...on PRISMIC_Homepage {
                    _meta {
                      type
                    }
                  }
                }
                link_label
              }
            }
          }
        }
      }
    }
  }
}
`

export default Homepage

Those of you with sharp eyes might have noticed that we completely removed the MainContent component. That was the hardcoded content for our homepage and we no longer need it. Remove it from your Homepage component and feel free to completely remove it from your project files.

If you refresh your homepage in the browser, you should now see something like this:

Homepage slice types list

This is great, now we just need to add our Slice components and replace the Slice type text with the actual Slice content.

Create the Slice components

Start by creating a new folder in the src/components directory with the name slices. In the new slices folder, create the following files:

  • FullWidthImage.js
  • ImageGallery.js
  • ImageHighlight.js
  • Quote.js
  • Text.js

Then paste each of the following components into your new files.

  • FullWidthImage.js
  • ImageGallery.js
  • ImageHighlight.js
  • Quote.js
  • Text.js
Copy
import React from 'react'

const FullWidthImage = ({ slice }) => (
  <section className="full-width-image content-section">
    <img
      src={slice.primary.full_width_image.url}
      alt={slice.primary.full_width_image.alt}
    />
  </section>
)

export default FullWidthImage
Copy
import React from 'react'
import { Link } from 'gatsby'
import { RichText } from 'prismic-reactjs'
import { linkResolver } from 'gatsby-source-prismic-graphql'

const ImageGallery = ({ slice }) => (
  <section className="image-gallery content-section">
    <RichText render={slice.primary.gallery_title} />
    <div className="gallery">
      {slice.fields.map((galleryItem, index) => (
        <div className="gallery-item" key={`gallery-item=${index}`}>
          <img
            src={galleryItem.image.url}
            alt={galleryItem.image.alt}
          />
          <RichText render={galleryItem.image_description} />
          {galleryItem.link && galleryItem.link._meta
            ? (
              <p className="gallery-link">
                <Link to={linkResolver(galleryItem.link._meta)}>
                  {RichText.asText(galleryItem.link_label)}
                </Link>
              </p>
            )
            : null}
        </div>
      ))}
    </div>
  </section>
)

export default ImageGallery
Copy
import React from 'react'
import { Link } from 'gatsby'
import { RichText } from 'prismic-reactjs'
import { linkResolver } from 'gatsby-source-prismic-graphql'

const ImageHighlight = ({ slice }) => (
  <section className="highlight content-section">
    <div className="highlight-left">
      <RichText render={slice.primary.title} />
      <RichText render={slice.primary.description} />
      {slice.primary.link && slice.primary.link._meta
        ? (
          <p>
            <Link to={linkResolver(slice.primary.link._meta)}>
              {RichText.asText(slice.primary.link_label)}
            </Link>
          </p>
        )
        : null}
    </div>
    <div className="highlight-right">
      <img
        src={slice.primary.featured_image.url}
        alt={slice.primary.featured_image.alt}
      />
    </div>
  </section>
)

export default ImageHighlight
Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'

const Quote = ({ slice }) => (
  <section className="content-section quote">
    <blockquote>{RichText.asText(slice.primary.quote)}</blockquote>
  </section>
)

export default Quote
Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'
import { linkResolver } from 'gatsby-source-prismic-graphql'

const Text = ({ slice }) => {
  const columnClass = slice.primary.columns === '2 Columns'
    ? 'text-section-2col'
    : 'text-section-1col'

  return (
    <section className={`content-section ${columnClass}`}>
      <RichText
        render={slice.primary.content}
        linkResolver={linkResolver}
      />
    </section>
  )
}

export default Text

We won't be going into details on this, but we encourage you to explore the content models and the component code for each of these slices to see how each one works.

Update the SliceZone component

Now that we have our Slice components, let's update the SliceZone component to use these. Open your src/components/SliceZone.js file and replace the code there with this.

Copy
import React from 'react'
import FullWidthImage from './slices/FullWidthImage'
import ImageGallery from './slices/ImageGallery'
import ImageHighlight from './slices/ImageHighlight'
import Quote from './slices/Quote'
import Text from './slices/Text'

const SliceZone = ({ sliceZone }) => {
  const sliceComponents = {
    full_width_image: FullWidthImage,
    image_gallery: ImageGallery,
    image_highlight: ImageHighlight,
    quote: Quote,
    text: Text,
  }

  const sliceZoneContent = sliceZone.map((slice, index) => {
    const SliceComponent = sliceComponents[slice.type]
    if (SliceComponent) {
      return <SliceComponent slice={slice} key={`slice-${index}`} />
    }
    return null
  })

  return (
    <main className="container">
      {sliceZoneContent}
    </main>
  )
}

export default SliceZone

You can see now that we're importing the Slice components, then doing a map over the Slice Zone and outputting the correct Slice component based on the slice.type. If you refresh your homepage, you should now see your content being displayed correctly. Now our Homepage content is coming entirely from Prismic!

Test the new content

To test that the content is coming from Prismic, do the following:

  1. Go to your Prismic repository and open the homepage document.
  2. Make a change to some of your Slice Zone content.
  3. Save and publish your changes.
  4. In your terminal, stop the current Gatsby server by pressing CTRL + C.
  5. Relaunch your server by running npm start.

This will re-build your site and update the content from your Prismic repository. When the build is complete, you can refresh the homepage and should see your updated content.

Previews

In a later article we will talk about how to setup Previews. So in the future, this process of testing new content will be much easier.

Next steps

Next up we will be updating the project to have the content pages (the About & More Info pages) built dynamically from Prismic.

Adding repeatable pages →