Add the navigation

Welcome to the fifth article in the getting started with Prismic & Gatsby tutorial series. We'll be walking through the steps to convert the top navigation from being hardcoded to being dynamically created and filled with 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.

See the Navigation model

Re-launch your site by running npm start. Looking at the top navigation, you can see that the model will be pretty simple. We don't know how many links we might need in the future, so we'll need to use a Group field to handle our links. Inside the Group field, we'll need:

  • A Link field to link to internal content
  • A Rich text field for the labels.

Look at the content model in Prismic

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 Navigation type. Here you can see how the model 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 to wait until the end of this step by step tutorial.

The navigation 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 Navigation document. Here we can see what it looks like with our live content.

The navigation content in Prismic

You can click on the "Add a new element in top_navigation" button to see how easy it is to add a new link to the navbar. Now that we know how this is modeled, let's add this to our Gatsby project.

1. Test the navigation query

Run the project with npm start, and take a look at your API Explorer at http://localhost:8000/__graphql. Once there, input this GraphQL query in the left-hand panel:

Copy
{
  prismicNavigation {
    data {
      top_navigation {
        link {
          type
          uid
          url
        }
        link_label {
          raw
        }
      }
    }
  }
}

You can run the query by pressing the "play" button ▷ at the top, showing you the query results on the right. Read, Anatomy of a query.

2. Render the results

We're going to be using a Fragment to query data in the Header component. For that, we'll need to update three files:

  1. The header component at src/components/Header.js
  2. The page template src/templates/Page.js
  3. The homepage at src/pages/index.js

Let's do that now, click on the corresponding tabs for each file and replace the existing code of your files in your project with the following:

  • Header.js
  • index.js
  • Page.js
Copy
import React from 'react'
import { Link, graphql } from 'gatsby'
import { RichText } from 'prismic-reactjs'

const Header = ({ isHomepage, navigation }) => {
  if (!navigation) return null
  const homepageClass = isHomepage ? 'homepage-header' : ''
  const topNav = navigation.data.top_navigation

  return (
    <header className={`site-header ${homepageClass}`}>
      <Link to="/">
        <div className="logo">Example Site</div>
      </Link>
      <nav>
        <ul>
          {topNav.map((navItem, index) => {
            return (
              <li key={`link-${index}`}>
                <Link to={navItem.link.url}>
                  {RichText.asText(navItem.link_label.raw)}
                </Link>
              </li>
            )
          })}
        </ul>
      </nav>
    </header>
  )
}

export const query = graphql`
  fragment HeaderQuery on PrismicNavigation {
    data {
      top_navigation {
        link {
          type
          uid
          url
        }
        link_label {
          raw
        }
      }
    }
  }
`

export default Header
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,
  }

  const prismicNavigation = data.prismicNavigation

  return (
    <Layout isHomepage navigation={prismicNavigation}>
      <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
                  }
                }
              }
            }
          }
        }
      }
    }
    prismicNavigation {
      ...HeaderQuery
    }
  }
`

export default Homepage
Copy
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import SliceZone from '../components/SliceZone'

const Page = ({ data }) => {
  if (!data) return null
  const document = data.allPrismicPage.edges[0].node
  const prismicNavigation = data.prismicNavigation

  const capitalizeFirstLetter = (input) => {
    return input[0].toUpperCase() + input.slice(1)
  }

  return (
    <Layout navigation={prismicNavigation}>
      <SEO title={capitalizeFirstLetter(document.uid)} />
      <SliceZone sliceZone={document.data.body} />
    </Layout>
  )
}

export const query = graphql`
  query PageQuery($uid: String) {
    allPrismicPage(filter: { uid: { eq: $uid } }) {
      edges {
        node {
          uid
          data {
            body {
              ... on PrismicPageBodyText {
                slice_type
                primary {
                  columns
                  content {
                    raw
                  }
                }
              }
              ... on PrismicPageBodyQuote {
                slice_type
                primary {
                  quote {
                    raw
                  }
                }
              }
              ... on PrismicPageBodyFullWidthImage {
                slice_type
                primary {
                  full_width_image {
                    url
                    thumbnails
                  }
                }
              }
              ... on PrismicPageBodyImageGallery {
                slice_type
                primary {
                  gallery_title {
                    raw
                  }
                }
                items {
                  image {
                    url
                    thumbnails
                    alt
                  }
                  image_description {
                    raw
                  }
                  link {
                    url
                    type
                    uid
                  }
                  link_label {
                    raw
                  }
                }
              }
              ... on PrismicPageBodyImageHighlight {
                slice_type
                primary {
                  featured_image {
                    url
                    thumbnails
                    alt
                  }
                  title {
                    raw
                  }
                  description {
                    raw
                  }
                  link {
                    url
                    type
                    uid
                  }
                  link_label {
                    raw
                  }
                }
              }
            }
          }
        }
      }
    }
    prismicNavigation {
      ...HeaderQuery
    }
  }
`

export default Page

Now when you refresh your site, your top navigation will be pulling its content from Prismic! There are a couple of things to note here about Link Resolving.

Link Resolving

You may or may not remember that we added this to our project back in the second article in the series. If it didn't make any sense then, hopefully, it is more clear now.

The Link Resolver is a simple function that takes in a Prismic Link field and returns the corresponding URL for that page in your site. It lives in src/utils/linkResolver.js in your project and looks like this.

Copy
const linkResolver = (doc) => {
  if (doc.type === 'page') {
    return `/${doc.uid}`
  }
  return '/'
}

module.exports = linkResolver

As you can see here, if the link passed to the Link Resolver is of the type "page", then it will generate the URL using the page's UID value (such as '/about' or '/more-info'). Anything else (the homepage) will return the website's root: '/'.

If you're curious to learn more, check out the Link Resolving article.

Previews & deploys with Gatsby Cloud →