Component-level data fetching in JSS Next.js apps

Abstract

Overview of how JSS Next.js apps retrieve component-level data and how it can be accessed

Next.js provides the ability to pre-render pages at build time or on each request using page-level data fetching strategies for each pre-rendering form.

If you want to fetch component-specific data at the component level, you can use a mechanism similar to page-level data fetching, by implementing and exporting getStaticProps for static generation (SG), or getServerSideProps for server-side rendering (SSR).

The SitecorePagePropsFactory class uses an instance of the ComponentPropsService class that helps to identify which components require retrieval of data. The ComponentPropsService is provided by the NPM package @sitecore-jss/sitecore-jss-nextjs.

The ComponentPropsService

The ComponentPropsService accepts the following parameters:

  • componentModule - a function returning a Next.js component using the componentName argument. You can find the componentModule function definition in the file src/temp/componentFactory. If not present, generate it by running scripts/bootstrap.ts.

  • layoutData - Layout Service data for your page.

  • context - The Next.js SSG or SSR context.

The ComponentPropsService traverses the layout service data and looks at all the renderings. To find the components that require data fetching, the service spies on the component using the rendering.componentName against the list of component registrations in the src/temp/componentFactory file.

If the component defines and exports the functions getStaticProps or getServerSideProps, the ComponentPropsService runs the function. After applying all side-effects, it stores the component data in a componentProps object in the format { [rendering.uid]: data }.

For example, the following component defined a data fetching function and accesses the component data with the help of useComponentProps and the rendering.uid.

Note

This example component contains an implementation of both getStaticProps and getServerSideProps. In a real component, you must define only one of them, depending on the rendering mode you chose.

import {
  Text,
  RichText,
  Field,
  GetServerSideComponentProps,
  GetStaticComponentProps,
  useComponentProps,
  ComponentRendering,
} from '@sitecore-jss/sitecore-jss-nextjs';

type PostEntity = {
  title: string;
  body: string;
};

type ContentBlockProps = {
  rendering: ComponentRendering;
  fields: {
    heading: Field<string>;
    content: Field<string>;
  };
};

const ContentBlock = ({ fields, rendering }: ContentBlockProps): JSX.Element => {
  const externalData = rendering.uid ? useComponentProps<PostEntity>(rendering.uid) : undefined;

  return (
    <>
      <Text tag="h2" className="display-4" field={fields.heading} />

      <RichText className="contentDescription" field={fields.content} />

      {externalData && (
        <div>
          <h1>{externalData?.title}</h1>
          <p>{externalData?.body}</p>
        </div>
      )}
    </>
  );
};

const fetchPost = () =>
  fetch('https://jsonplaceholder.typicode.com/posts/1').then((res) => res.json());

export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData, context) => {
  const post = await fetchPost();

  return post;
};

export const getServerSideProps: GetServerSideComponentProps = async (rendering, layoutData) => {
  const post = await fetchPost();

  return post;
};

export default ContentBlock;