Component-level data fetching in JSS Next.js apps

Version:

Next.js can prerender pages at build time or on each request using page-level data fetching strategies for each 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 the getStaticProps function for static generation (SG) or the getServerSideProps function for server-side rendering (SSR).

Danger

Do not include secrets or sensitive information in component-level getStaticProps and getServerSideProps functions. These functions are included in the client-side bundle. Also included in the client bundle are any imports the functions depend on, even if the client-side code does not use the imports.

This does not affect page-level getStaticProps and getServerSideProps functions.

Watch the following video for a demonstration of how component-level data fetching works in a JSS app:

The ComponentPropsService class

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

The ComponentPropsService class 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 src/temp/componentFactory file. 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 class traverses the layout service data and looks at all the renderings. To find the components that require data fetching, the service watches the component using the rendering.componentName property 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 class 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 defines a data-fetching function and displays the injected component props by reading them from the component props context using the rendering UID.

Note

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

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

type ComponentProps = {
  rendering: ComponentRendering;
  params: ComponentParams;
};

type ContentBlockProps = ComponentProps & {
  fields: {
    heading: Field<string>;
    content: Field<string>;
  };
};

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

const ContentBlock = (props: ContentBlockProps): JSX.Element => {
  const { fields, rendering } = props;

  // Component-level fetched data is stored by rendering.uid
  const post = useComponentProps<Post>(rendering.uid);

  return (
    <div className="contentBlock">
      <Text tag="h2" className="contentTitle" field={fields.heading} />
      <RichText className="contentDescription" field={fields.content} />

      {/* Demonstrate using fetched data returned by getStaticProps/getServerSideProps */}
      {post && (
        <section className="contentPost">
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </section>
      )}
    </div>
  );
};

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

// Note: Define only ONE of these in a real component, based on rendering mode.
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 withDatasourceCheck()<ContentBlockProps>(ContentBlock);
If you have suggestions for improving this article, let us know!