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.
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.
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.
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.
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.
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:
{
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.
We're going to be using a Fragment to query data in the Header component. For that, we'll need to update three files:
- The header component at src/components/Header.js
- The page template src/templates/Page.js
- 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
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
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
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.
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.
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.