How Pages work in Next.js

When using Agility CMS, we believe Editors should have full control over the pages on their website and should not need to rely on a developer. The agilitycms-next-starter makes it easy to source content and also generates the pages of your website based on your sitemap in Agility CMS.

Editors in the CMS can control what pages are available, what the URLs are, what page template they're using, and exactly what UI Components (Modules) make up each and every page.

Page Generation

The following example is based on the Blog Template instance. Don't have one? Sign up for one!

Screen_Shot_2021-04-09_at_11.22.05_AM.png

The above figure represents a sitemap of Pages as well as the UI Components (Modules) that are on each page - all are managed in the CMS. 

This means that when a build occurs in your Next.js site, the following pages will be auto-generated for you:

  • /
  • /blog
  • /blog-posts/* - your dynamic page route for all of your blog posts (i.e. /blog/my-first-post)
  • /about

Page Rendering

When a page is being generated during build time, the final step is rendering the page to HTML. In the agilitycms-nextjs-starter site, the [...slug].js file located within the pages directory takes care of this for you. It returns a Layout.js component that passes all necessary props to render each page.

Sample source code for /pages/[...slug].js

import Layout from "components/common/Layout";
import { getAgilityPageProps, getAgilityPaths } from "@agility/nextjs/node";
import { getModule } from "components/agility-pageModules";
import SiteHeader from "components/common/SiteHeader";

// getStaticProps function fetches data for all of your Agility Pages and Next.js will pre-render these pages at build time
export async function getStaticProps({
  preview,
  params,
  locale,
  defaultLocale,
  locales,
}) {
  // place all global here
  const globalComponents = {
    header: SiteHeader,
  };

  const agilityProps = await getAgilityPageProps({
    preview,
    params,
    locale,
    getModule,
    defaultLocale,
    globalComponents,
  });

  if (!agilityProps) {
    // We throw to make sure this fails at build time as this is never expected to happen
    throw new Error(`Page not found`);
  }

  return {
    // return all props
    props: agilityProps,

    // Next.js will attempt to re-generate the page when a request comes in, at most once every 10 seconds
    // Read more on Incremental Static Regenertion here: https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
    revalidate: 10,
  };
}

// Next.js will statically pre-render all the paths from Agility CMS
export async function getStaticPaths({ locales, defaultLocale }) {
  //get the paths configured in agility
  let agilityPaths = await getAgilityPaths({
    preview: false,
    locales,
    defaultLocale,
  });

  return {
    paths: agilityPaths,
    fallback: true,
  };
}

const AgilityPage = (props) => {
  return <Layout {...props} />;
};

Page Rendering Process

  1. getStaticProps is a function in Next.js that will pre-render a page at build time using the props that it returns. getAgilityPageProps will get all the context for a particular page and return it as your props
    export async function getStaticProps({
      preview,
      params,
      locale,
      defaultLocale,
      locales,
    }) {
      // place all global here
      const globalComponents = {
        header: SiteHeader,
      };
    
      const agilityProps = await getAgilityPageProps({
        preview,
        params,
        locale,
        getModule,
        defaultLocale,
        globalComponents,
      });
    
      if (!agilityProps) {
        // We throw to make sure this fails at build time as this is never expected to happen
        throw new Error(`Page not found`);
      }
    
      return {
        // return all props
        props: agilityProps,
    
        // Next.js will attempt to re-generate the page when a request comes in, at most once every 10 seconds
        // Read more on Incremental Static Regenertion here: https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
        revalidate: 10,
      };
    }
    

    Instant Builds?

    Take note of the revalidate tag. When your Next.js and Agility CMS site is hosted on Vercel, the revalidate tag turns your deployment into an instant build machine - meaning your pages are built as they change!

    Learn more about ISR (Incremental Static Regeneration)

  2. Content Items stored in Agility that you want to appear or have access to globally across your site such as a Site Header or Site Footer component should be imported and placed within the globalComponents object.
    // place all global here 
     const globalComponents = {
       header: SiteHeader,
     };
  3. The props are then passed into the AgilityPage component which returns a Layout component that will handle your Page Templates.
    const AgilityPage = (props) => {
        return <Layout {...props} />;
    }; 
  4. The Layout.js component located within the /components/common directory resolves which Page Template should be rendered (<AgilityPageTemplate />) and passes the necessary content as props. The Layout.js component will also handle your site SEO and Preview.
    import { getPageTemplate } from "components/agility-pageTemplates";
    import { handlePreview } from "@agility/nextjs";
    import { useRouter } from "next/router";
    import Error from "next/error";
    import PreviewBar from "./PreviewBar";
    import SEO from "./SEO";
    import SiteHeader from "./SiteHeader";
    import SiteFooter from "./SiteFooter";
    import LoadingWidget from "./LoadingWidget";
    
    // set up handle preview
    const isPreview = handlePreview();
    
    function Layout(props) {
      const {
        page,
        sitemapNode,
        dynamicPageItem,
        notFound,
        pageTemplateName,
      } = props;
    
      // If the page is not yet generated, this will be displayed
      // initially until getStaticProps() finishes running
      const router = useRouter();
      if (router.isFallback) {
        return ;
      }
    
      // if page not found, throw 404
      if (notFound === true) {
        return ;
      }
    
      const AgilityPageTemplate = getPageTemplate(pageTemplateName);
    
      if (dynamicPageItem?.seo?.metaDescription) {
        page.seo.metaDescription = dynamicPageItem.seo.metaDescription;
      }
    
      return (
        <>
          <SEO
            title={sitemapNode?.title}
            description={page.seo.metaDescription}
            keywords={page.seo.metaKeywords}
            metaHTML={page.seo.metaHTML}
          />
          <div id="site-wrapper">
            {isPreview && <LoadingWidget message="Loading Preview Mode" />}
            {!isPreview && (
              <div id="site">
                <PreviewBar {...props} />
                <div className="flex flex-col min-h-screen">
                  <SiteHeader {...props} />
                  <main className="flex-grow">
                    <AgilityPageTemplate {...props} />
                  </main>
                  <SiteFooter {...props} />
                </div>
              </div>
            )}
          </div>
        </>
      );
    }
    
    export default Layout;
    
  5. Page Templates in Agility CMS use the <ContentZone /> component from the @agility/nextjs package and the getModule function from the /components/agility-pageModules directory to identify where modules for specific zones should be rendered within the Page Template. Here's an example for a "Main Page Template":

    import React from "react";
    import { ContentZone } from "@agility/nextjs";
    import { getModule } from "components/agility-pageModules";
    
    const MainTemplate = (props) => {
      return (
        <ContentZone name="MainContentZone" {...props} getModule={getModule} />
      );
    };
    
    export default MainTemplate;
    
  6. Within each <ContentZone /> component defined in the template, each module that exists on the content zone in the CMS is resolved to a react component and rendered in-place.

    Here's an example from the Rich Text Area Module found within the ./components/agility-pageModules directory:

    import React from "react";
    import { renderHTML } from "@agility/nextjs";
    
    const RichTextArea = ({ module }) => {
      // get module fields
      const { fields } = module;
      return (
        <div className="relative px-8">
          <div className="max-w-2xl mx-auto my-12 md:mt-18 lg:mt-20">
            <div
              className="prose max-w-full mx-auto"
              dangerouslySetInnerHTML={renderHTML(fields.textblob)}
            />
          </div>
        </div>
      );
    };
    
    export default RichTextArea;
    

    Learn How Page Modules work in Next.js

3 out of 3 found this helpful

Comments

0 comments

Please sign in to leave a comment.