Navbars, footers and menus

Querying menu documents is not something that you'll do using Slice Machine for the moment as these types of elements usually live outside the Slice Zone and are something that you don't need to query on every page.

You'll only want to query these type of documents once, so a good approach is to create a Layout that queries these elements and shares the data everywhere in your application. We'll show you the best practice for doing this below.

Model your menu in Prismic.

In this example, we have created a Single type for the main menu. It has a title to the menu and a group field. In the group fields repeatable zone we added a link field and a label field, this way you can add as many links as you need. From here you can create a new menu document in your Writing room and add your content.

{
  "Main" : {
    "title" : {
      "type" : "StructuredText",
      "config" : {
        "placeholder" : "Menu title...",
        "single" : "heading1"
      }
    },
    "menu_links" : {
      "type" : "Group",
      "config" : {
        "fields" : {
          "label" : {
            "type" : "StructuredText",
            "config" : {
              "single" : "paragraph",
              "label" : "Link Label",
              "placeholder" : "Link Label..."
            }
          },
          "link" : {
            "type" : "Link",
            "config" : {
              "label" : "Link",
              "placeholder" : "Select a Link..."
            }
          }
        },
        "label" : "Menu Links"
      }
    }
  }
}

Create your menu query.

First, open the _app.js file. Then query for the single instance of the custom type "menu" using the query helper function getSingle from the Javascript development kit. This query will be located inside the getInitialProps fetching method.

Then, pass the menu as props to the <Component /> in the render method.

import React from 'react'
import NextApp from 'next/app'
import { theme } from 'essential-slices'
import { ThemeProvider, BaseStyles } from 'theme-ui'
import { Client } from "../prismic";

export default class App extends NextApp {
  static async getInitialProps(appCtx) {
    const client = Client();
    const menu = (await client.getSingle("menu")) || {};
    return {
      props: {
        menu: menu
      },
    };
  }

  render() {
    const { Component, pageProps, props } = this.props
    return (
      <ThemeProvider theme={theme}>
        <BaseStyles>
            <Component {...pageProps} menu={props.menu} />
        </BaseStyles>
      </ThemeProvider>
    )
  }
}

Create the Layout components

There are 3 components that make up this Layout:

  • Layout.js
  • Navbar.js
  • DocLink.js (to resolve your links)

Create a components folder and add these files inside of it.

Layout.js

The Layout will hold both the Navbar and main component to create a general template for every page, this is also where you would add a footer if you wanted one. Retrieve the menu from the props and pass it to the <Navbar /> component.

Make sure that all the imports match your project routes.

💡Note

Head is different from the Navbar component

import React from "react";
import Head from "next/head";
import Navbar from "./Navbar";

const Layout = ({ menu, children }) => {
  const menuLinks = menu.data.menu_links;
  return (
    <div>
      <Head>
        <title>Slice Machine - NextJS</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Navbar menuLinks={menuLinks} />
      <main>{children}</main>
    </div>
  );
};

export default Layout;

This component will be the Navigation bar of your site. Retrieve the menu from the props and map the menu links to pass them as props to the DocLink component.

Make sure that all the imports match your project routes.

import React from "react";
import { RichText } from 'prismic-reactjs'
import DocLink from "./DocLink"

const Navbar = ({ menuLinks = [] }) => (
    <header className="site-header">
      <Links menuLinks={menuLinks} />
    </header>
);

const Links = ({menuLinks}) => {
  if (menuLinks) {
    return (
      <nav>
        <ul>
          {menuLinks.map((menuLink, index) => (
            <li key={`menulink-${index}`}>
              <DocLink link={menuLink.link}>
                {RichText.asText(menuLink.label)}
              </DocLink>
            </li>
          ))}
        </ul>
      </nav>
    )
  }
  return null
}

export default Navbar;

This file will help you resolve the links coming from Prismic

import React from "react";
import { default as NextLink } from "next/link";
import { Link } from "prismic-reactjs";

import { linkResolver, hrefResolver } from "./../prismic";

// Main DocLink component function for generating links to other pages on this site
const DocLink = ({ children, link, linkClass }) => {
  if (link) {
    const linkUrl = Link.url(link, linkResolver);

    // If the link is an internal link, then return a NextLink
    if (link.link_type && link.link_type === "Document") {
      return (
        <NextLink as={linkUrl} href={Link.url(link, hrefResolver)}>
          <a className={linkClass}>{children}</a>
        </NextLink>
      );
    }

    // Otherwise return a normal anchor element
    return (
      <a className={linkClass} href={linkUrl}>
        {children}
      </a>
    );
  }
  return null;
};

export default DocLink;

⚠️Link Resolver routes

Make sure you update your linkResolver & hrefResolver in your prismic.js file for any new route that you're adding.

Add the Layout to your pages and pass them props

Now that you've built your Layout component, you can wrap all elements in the render of your page files, and pass the menu as props.

Here we give you an example of the [uid].js file, but you can add it to every other page that you create, like the index.js that will be your homepage.

import { Client } from "../prismic";
import SliceZone from "next-slicezone";
import { useGetStaticProps, useGetStaticPaths } from "next-slicezone/hooks";

import resolver from "../sm-resolver.js";

import Layout from "./../components/Layout";

const Page = (props) => {
  return (
    <Layout menu={props.menu}>
      <SliceZone {...props} resolver={resolver} />;
    </Layout>
  );
};

// Fetch content from prismic
export const getStaticProps = useGetStaticProps({
  client: Client(),
  uid: ({ params }) => params.uid,
});

export const getStaticPaths = useGetStaticPaths({
  client: Client(),
  type: "page",
  fallback: true, // process.env.NODE_ENV === 'development',
  formatPath: ({ uid }) => ({ params: { uid } }),
});

export default Page;

That's it, now you have a Layout with a menu that you can modify to match your likings.

9 Rue de la Pierre Levée, 75011 Paris