Create Homepage Slices

Welcome to the third article in the getting started with Prismic and Gatsby tutorial series. We'll be walking through the steps required to convert the content from the top homepage from hard-coded to use content from Prismic.


🕙  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.

Content modeling

Run npm start to re-launch your site and look at the homepage. As you can see, we can split the content into components. We want this page's main content to be intuitive so that our content authors can mix and match the page elements to build the homepage. Prismic's Slices will be perfect for this!

The models for these Slices already exist in your Prismic repository; click on Custom Types, and select the Homepage type to see how the configuration of the Slice Zone.

Then, click the Documents button and select the Homepage to view the live version of the Slices. They should look something like this:

⚠️ 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. However, if you wish to change anything, we highly recommend you wait until the end of this step-by-step tutorial and read the dedicated plugin configuration article.

Now that we know how to model Slices let's learn how to add them to your Gatsby project.

1. Test the query

Run the project with npm start. Next, open the GraphQL Playground a thttp://localhost:8000/__graphql and paste this GraphQL query. In this example, we're going to retrieve the Quote Slice in our Custom Type to check that everything is working:

Copy
query MyQuery {
  prismicHomepage {
    data {
      body {
        ...on PrismicHomepageDataBodyQuote {
          slice_type
          primary {
            quote {
              raw
            }
          }
        }
      }
    }
  }
}

Run the query by pressing the "play" button ▷ at the top to see the query results on the right. We added 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 items and primary 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.

Instead of adding this entire query in our page files, we will use fragments to make more readable and segmented queries for this tutorial. Thus, each component will have its own query. If you want to know more about how the fragments work, read the dedicated guide:

2. Render the results

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

1. Add the SliceZone

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

Copy
// SliceZone.js file

import * as React from 'react'

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

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 and the query with the fragments for each Slice component:

Copy
// index.js file

import * as React from 'react'
import { graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

import { Layout } from '../components/Layout'
import { Seo } from '../components/Seo'
import { HomepageBanner } from '../components/HomepageBanner'
import { SliceZone } from '../components/SliceZone'

const HomeTemplate = ({ data }) => {
  if (!data) return null
  const doc = data.prismicHomepage.data

  return (
    <Layout isHomepage>
      <Seo title="Home" />
      <HomepageBanner
        title={RichText.asText(doc.banner_title.raw)}
        description={RichText.asText(doc.banner_description.raw)}
        linkUrl={doc.banner_link.url}
        linkLabel={RichText.asText(doc.banner_link_label.raw)}
        backgroundUrl={doc.banner_background.url}
      />
      <SliceZone sliceZone={doc.body} />
    </Layout>
  )
}

export const query = graphql`
  query MyQuery {
    prismicHomepage {
      data {
        banner_title {
          raw
        }
        banner_description {
          raw
        }
        banner_link {
          url
          type
          uid
        }
        banner_link_label {
          raw
        }
        banner_background {
          url
        }
        body {
          ...on PrismicSliceType {
            slice_type
          }
          ...HomepageDataBodyText
          ...HomepageDataBodyQuote
          ...HomepageDataBodyFullWidthImage
          ...HomepageDataBodyImageGallery
          ...HomepageDataBodyImageHighlight
        }
      }
    }
  }
`

export default HomeTemplate

Those of you with sharp eyes might have noticed that we removed the MainContent component. That was the hard-coded 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.

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

Example view of the Homepage slice types list
Example view of the Homepage slice types list

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

3. Create the Slice components

Create a new folder in the 〜/src/slices directory. In the new slices folder, create the following files:

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

Now, we'll create the queries for each Slice and the code to display the data. Paste each of the following components into your new files:

  • FullWidthImage.js
  • ImageGallery.js
  • ImageHighlight.js
  • Quote.js
  • Text.js
Copy
// FullWidthImage.js file

import * as React from 'react'
import { graphql } from 'gatsby'

export 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 const query = graphql`
  fragment PageDataBodyFullWidthImage on PrismicPageDataBodyFullWidthImage {
    primary {
      full_width_image {
        url
      }
    }
  }
  fragment HomepageDataBodyFullWidthImage on PrismicHomepageDataBodyFullWidthImage {
    primary {
      full_width_image {
        url
      }
    }
  }
`
Copy
// ImageGallery.js file

import * as React from 'react'
import { Link, graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

export 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} />
            <p className="gallery-link">
              <Link to={galleryItem.link.url}>
                {RichText.asText(galleryItem.link_label.raw)}
              </Link>
            </p>
          </div>
        ))}
      </div>
    </section>
  )
}

export const query = graphql`
  fragment PageDataBodyImageGallery on PrismicPageDataBodyImageGallery {
    primary {
      gallery_title {
        raw
      }
    }
    items {
      image {
        url
        alt
      }
      image_description {
        raw
      }
      link {
        url
        type
        uid
      }
      link_label {
        raw
      }
    }
  }
  fragment HomepageDataBodyImageGallery on PrismicHomepageDataBodyImageGallery {
    primary {
      gallery_title {
        raw
      }
    }
    items {
      image {
        url
        alt
      }
      image_description {
        raw
      }
      link {
        url
        type
        uid
      }
      link_label {
        raw
      }
    }
  }
`
Copy
// ImageHighlight.js file

import * as React from 'react'
import { Link, graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

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

export const query = graphql`
  fragment PageDataBodyImageHighlight on PrismicPageDataBodyImageHighlight {
    primary {
      featured_image {
        url
        alt
      }
      title {
        raw
      }
      description {
        raw
      }
      link {
        url
        type
        uid
      }
      link_label {
        raw
      }
    }
  }
  fragment HomepageDataBodyImageHighlight on PrismicHomepageDataBodyImageHighlight {
    primary {
      featured_image {
        url
        alt
      }
      title {
        raw
      }
      description {
        raw
      }
      link {
        url
        type
        uid
      }
      link_label {
        raw
      }
    }
  }
`
Copy
// Quote.js file

import * as React from 'react'
import { graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

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

export const query = graphql`
  fragment PageDataBodyQuote on PrismicPageDataBodyQuote {
    primary {
      quote {
        raw
      }
    }
  }
  fragment HomepageDataBodyQuote on PrismicHomepageDataBodyQuote {
    primary {
      quote {
        raw
      }
    }
  }
`
Copy
// Text.js file

import * as React from 'react'
import { graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

import { CustomLink } from '../utils/CustomLink'

export 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}
        serializeHyperlink={CustomLink}
      />
    </section>
  )
}

export const query = graphql`
  fragment PageDataBodyText on PrismicPageDataBodyText {
    primary {
      columns
      content {
        raw
      }
    }
  }
  fragment HomepageDataBodyText on PrismicHomepageDataBodyText {
    primary {
      columns
      content {
        raw
      }
    }
  }
`

Each query component has two almost identical fragment queries. The only difference is the Custom type name which in these cases are Homepage and Page. These fragments help us to share the Slices and their components between Custom Types.

Also, if you remember, we added a Custom Link function in the second article that we are now using in the Text.js component. If your Rich text field has links to internal documents or external URLs, you'll use this function to render them appropriately with Gatsby Link, handle internal links, and <a> tags for external links. Then we use this function in the serializeHyperlink option of the <RichText> component.

4. Update the SliceZone

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

Copy
// SliceZone.js file 

import * as 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'

export 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>
}

We map over the Slice Zone and output 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. First, 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.


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Get in touch with us on our Community Forum.