Create homepage slices

Welcome to the third article in the getting started with Prismic & Gatsby tutorial series. We'll be walking through the steps required to convert the homepage main content 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 step, we recommend you start there to download the project, launch a Prismic repository, and install the required plugin and dependencies.

See the content model

We want this page's main content to be easily modifiable 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!

Re-launch your site by running npm start. Looking at the homepage, you can see we can divide the homepage into five sections.

Look at the content model in Prismic

These page sections have already been put into place in your Prismic repository. To see how they look, go to your Prismic repo, click on the Custom Types button in the left-hand navigation bar, and select the Homepage type. Here you can see how the Slice Zone was configured in Prismic.

⚠️ No need to modify the Custom types

You do not need to change anything in the Custom types of your repository. Just take a look at how it is set up. If you wish to change anything, we highly recommend you wait until the end of this step by step tutorial.

The Slice Zone content model in Prismic

Then, go back using the arrow button , click on the Documents button in the left-hand navigation bar, and select the Homepage document to view it live. 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 know how this is modeled. Let's add this to our homepage on the Gatsby website.

1. Test the query

Run the project with npm start and look at your API Explorer at http://localhost:8000/__graphql. Once there, input this GraphQL query in the left-hand panel (In this query, we use the Union type to specify the fields we need from each Slice type):

Copy
query Homepage {
  allPrismicHomepage {
    edges {
      node {
        data {
          banner_title {
            raw
          }
          banner_description {
            raw
          }
          banner_link {
            url
            type
            uid
          }
          banner_link_label {
            raw
          }
          banner_background {
            url
            thumbnails
            alt
          }
          body {
            ... on PrismicHomepageBodyText {
              slice_type
              primary {
                columns
                content {
                  raw
                }
              }
            }
            ... on PrismicHomepageBodyQuote {
              slice_type
              primary {
                quote {
                  raw
                }
              }
            }
            ... on PrismicHomepageBodyFullWidthImage {
              slice_type
              primary {
                full_width_image {
                  url
                  thumbnails
                }
              }
            }
            ... on PrismicHomepageBodyImageGallery {
              slice_type
              primary {
                gallery_title {
                  raw
                }
              }
              items {
                image {
                  url
                  thumbnails
                  alt
                }
                image_description {
                  raw
                }
                link {
                  url
                  type
                  uid
                }
                link_label {
                  raw
                }
              }
            }
            ... on PrismicHomepageBodyImageHighlight {
              slice_type
              primary {
                featured_image {
                  url
                  thumbnails
                  alt
                }
                title {
                  raw
                }
                description {
                  raw
                }
                link {
                  url
                  type
                  uid
                }
                link_label {
                  raw
                }
              }
            }
          }
        }
      }
    }
  }
}

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

You may have noticed that we added a few new types of fields; let's go over them:

  • Union type ...on: We use it to specify the Slices we want from each document.
  • slice_type: This field will allow us to know which component to render depending on it.
  • The primary and items inside Slices: These represent the repeatable and non-repeatable zones of the Slice. We specify all the fields we need to retrieve using their API IDs and their needed values.

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.

2. Render the results

Let's now render the results to produce the Slices.

1. Add the SliceZone

Add a new Slice Zone at src/component/SliceZone.js file and paste the following code:

Copy
import React from 'react'

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

export default SliceZone

This component will 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 index.js file.

2. Update the index.js file

Navigate to src/pages/index.js and update the query to add the SliceZone component to our template:

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 }) => {
  if (!data) return null
  const document = data.allPrismicHomepage.edges[0].node.data

  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`
  query Homepage {
    allPrismicHomepage {
      edges {
        node {
          data {
            banner_title {
              raw
            }
            banner_description {
              raw
            }
            banner_link {
              url
              type
              uid
            }
            banner_link_label {
              raw
            }
            banner_background {
              url
              thumbnails
              alt
            }
            body {
              ... on PrismicHomepageBodyText {
                slice_type
                primary {
                  columns
                  content {
                    raw
                  }
                }
              }
              ... on PrismicHomepageBodyQuote {
                slice_type
                primary {
                  quote {
                    raw
                  }
                }
              }
              ... on PrismicHomepageBodyFullWidthImage {
                slice_type
                primary {
                  full_width_image {
                    url
                    thumbnails
                  }
                }
              }
              ... on PrismicHomepageBodyImageGallery {
                slice_type
                primary {
                  gallery_title {
                    raw
                  }
                }
                items {
                  image {
                    url
                    thumbnails
                    alt
                  }
                  image_description {
                    raw
                  }
                  link {
                    url
                    type
                    uid
                  }
                  link_label {
                    raw
                  }
                }
              }
              ... on PrismicHomepageBodyImageHighlight {
                slice_type
                primary {
                  featured_image {
                    url
                    thumbnails
                    alt
                  }
                  title {
                    raw
                  }
                  description {
                    raw
                  }
                  link {
                    url
                    type
                    uid
                  }
                  link_label {
                    raw
                  }
                }
              }
            }
          }
        }
      }
    }
  }
`

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 index.js, and feel free to remove it from your project files completely.

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

Homepage slice types list

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

3. 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'

const ImageGallery = ({ slice }) => {
  return (
    <section className="image-gallery content-section">
      <RichText render={slice.primary.gallery_title.raw} />
      <div className="gallery">
        {slice.items.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.raw} />
            {galleryItem.link && galleryItem.link ? (
              <p className="gallery-link">
                <Link to={galleryItem.link.url}>
                  {RichText.asText(galleryItem.link_label.raw)}
                </Link>
              </p>
            ) : null}
          </div>
        ))}
      </div>
    </section>
  )
}

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

const ImageHighlight = ({ slice }) => (
  <section className="highlight content-section">
    <div className="highlight-left">
      <RichText render={slice.primary.title.raw} />
      <RichText render={slice.primary.description.raw} />
      {slice.primary.link && slice.primary.link ? (
        <p>
          <Link to={slice.primary.link.url}>
            {RichText.asText(slice.primary.link_label.raw)}
          </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.raw)}</blockquote>
  </section>
)

export default Quote
Copy
import React from 'react'
import { RichText } from 'prismic-reactjs'
import linkResolver from './../../utils/linkResolver'

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.raw}
        linkResolver={linkResolver}
      />
    </section>
  )
}

export default Text

4. 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.slice_type]
    if (SliceComponent) {
      return <SliceComponent slice={slice} key={`slice-${index}`} />
    }
    return null
  })

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

export default SliceZone

Now that we do a map over the Slice Zone and outputting the correct Slice component based on the slice.slice_type. If you refresh your homepage, you should now see your content displayed correctly. Now our Homepage content is coming entirely from Prismic!

3. 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 see your updated content.

Next steps

Next up, we will be updating the project to have the content pages built dynamically from Prismic.

Adding repeatable pages →