Effective Strategies for Integrating Sitecore Data with Custom Hooks and Static Props

Best Practices for Next.js and Sitecore Integration

April 18, 2024

By Sohrab Saboori

Efficient data retrieval and rendering are crucial for optimizing performance and enhancing user experience in contemporary web development. When working with Sitecore Headless and Next.js, particularly with GraphQL, developers have two primary options for integrating data: dynamically via custom hooks or at build time using getStaticProps. This blog explores these two methods, providing practical examples to effectively utilize GraphQL for fetching and integrating Sitecore data into Next.js projects.

Setting Up the GraphQL API Route in Next.js

Before diving into the methods of fetching data via getStaticProps and custom hooks, it's essential to set up an API route in your Next.js project to handle GraphQL requests to Sitecore. This setup involves creating an API handler under /api/graphql. Here’s a a sample code snippet that illustrates how you can fetch data from Sitecore:

import { GraphQLClient, gql } from 'graphql-request';
import config from '../../config';

// This function handles the API requests to the Sitecore GraphQL endpoint.
export default async function handler(req, res) {
  try {
    const { sitecorePath, sitecoreLanguage } = req.body;

    const categoryDetailDb = await GetItemFields(sitecorePath, sitecoreLanguage);

    return res.status(200).json(JSON.stringify(categoryDetailDb));
  } catch (error) {
    console.error('Error getting Sitecore data:', error);
    return res.status(500).json(JSON.stringify(error));
  }
}

// Fetches the fields for a specific item from Sitecore using GraphQL.
const GetItemFields = async (sitecorePath, sitecoreLanguage) => {
  const graphQLClient = new GraphQLClient(config.graphQLEndpoint);
  graphQLClient.setHeader('sc_apikey', config.sitecoreApiKey);

  const pageComponentsQuery = gql`
    query {
      item(
        path: "${sitecorePath}"
        language: "${sitecoreLanguage}"
      ) {
        id
        name
        fields {
          id
          name
          value
        }
      }
    }
  `;

  const data = await graphQLClient.request(pageComponentsQuery);

  return data;
};

After setting up the API handler to fetch data from Sitecore, you have two primary options to utilize this data within your Next.js project. These methods allow you to integrate the fetched data into your application effectively, each catering to different scenarios and requirements.

1. Utilizing getStaticProps for Static Site Generation

When building a Next.js application with Sitecore as your headless CMS, utilizing the getStaticProps function for static site generation is a powerful approach. This method is particularly effective for components that do not require real-time data updates and can benefit from Next.js's ability to serve pre-rendered pages quickly. A practical example of this is integrating getStaticProps directly in a component file, such as a carousel component. Here’s how you can implement this strategy:

For components like a carousel, which might display content fetched from Sitecore, you can embed the getStaticProps function directly in the component file (Carousel.tsx). This approach not only localizes the data fetching logic to the component but also enhances the modularity and maintainability of your application.

Here’s an example of how you can set up getStaticProps within your main carousel component file:

// Carousel.tsx
import { GetStaticComponentProps } from 'next';
import { Field, extractUUIDs } from 'some-sitecore-utility-library'; // Ensure to import or define these utilities

// Define the type structure for your props as needed
type CarouselProps = {
  carouselCards: CarouselCard[];
};

// Carousel component
const Carousel: React.FC<CarouselProps> = ({ carouselCards }) => {
  return (
    <div className="carousel-container">
      {carouselCards.map(card => (
        <div key={card.id} className="carousel-item">
          <h2>{card.title}</h2>
          <p>{card.description}</p>
          {/* other markup for displaying carousel content */}
        </div>
      ))}
    </div>
  );
};

// Fetch data at build time using getStaticProps
export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData) => {
  const language = layoutData?.sitecore?.context?.language ?? 'en';
  const carouselCardsFieldValue = rendering.fields?.carouselCards as Field<string>;
  const uuids = extractUUIDs(carouselCardsFieldValue.value);
  const carouselCards = await fetchMultipleCarouselCards(uuids, language);

  return {
    props: {
      carouselCards,
    },
  };
};

// Function to fetch multiple carousel cards from Sitecore
const fetchMultipleCarouselCards = async (uuids?: string[], language?: string) => {
  if (!uuids || uuids.length === 0) {
    return [];
  }

  const fetchedItems = await Promise.all(
    uuids.map(async (uuid) => {
      const url =`${process.env.PUBLIC_URL}/api/graphql/GetCarouselCards`;

      const response = await fetch(url, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({ sitecorePath: uuid, sitecoreLanguage: language ?? 'en' }),
      });

      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }

      const result = await response.json();
      return JSON.parse(result);
    })
  );

  return fetchedItems.filter(item => item !== null && item?.data?.datasource !== null);
};

export default Carousel;

In this example, getStaticProps fetches data for carousel cards based on UUIDs extracted from Sitecore. It handles multiple fetches concurrently with Promise.all, making it efficient even when multiple requests are required.

2. Dynamic Data Fetching with Custom Hooks

For dynamic scenarios where data needs may change based on user interactions or other runtime conditions, custom hooks in React offer a flexible and powerful way to fetch and manage data dynamically. This is particularly useful for components in a Next.js project integrated with Sitecore that require real-time updates or user-specific data.

Here is a basic example of a custom hook designed to fetch carousel data dynamically from Sitecore using GraphQL in a Next.js application:

import { useState, useEffect } from 'react';

const useCarouselData = (uuids, language) => {
  const [carouselData, setCarouselData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!uuids || uuids.length === 0) {
      setCarouselData([]);
      return;
    }

    const fetchData = async () => {
      setIsLoading(true);
      setError(null);

      try {
        const url  `${process.env.PUBLIC_URL}/api/graphql/GetCarouselCards`;

        const response = await fetch(url, {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({ sitecorePath: uuids.join(','), sitecoreLanguage: language }),
        });

        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }

        const result = await response.json();
        setCarouselData(result);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, [uuids, language]);

  return { carouselData, isLoading, error };
};

This hook manages the state for loading, error handling, and the fetched data. It updates the component, consuming it based on the given parameters and changes to these parameters.

For a detailed explanation of how to use custom hooks effectively in your Next.js projects integrated with Sitecore XM Cloud, including advanced patterns and best practices, please refer to my comprehensive guide available at Custom Hooks for Next.js in Sitecore XM Cloud.

Balancing Static and Dynamic Data Strategies in Next.js with Sitecore

When integrating Sitecore data into your Next.js projects, the choice between static generation with getStaticProps and dynamic data fetching with custom hooks hinges on your application's specific needs and scenarios.

Static Generation with getStaticProps: This method excels when the content does not change frequently and can be pre-rendered at build time. Static generation provides unmatched performance benefits, as it serves pre-built HTML to the client, reducing server load and speeding up page load times. It is particularly advantageous for SEO, as search engines can easily index static content.

Dynamic Fetching with Custom Hooks: For parts of your application that require real-time data updates or need to respond to user-specific contexts, custom hooks are the ideal solution. They offer the flexibility to fetch data on the client side, adapting to changes in user state or application context without the need to rebuild and redeploy the entire site.

Leveraging both static generation and dynamic fetching allows you to create robust, efficient, and user-friendly web applications. Static generation can be used for the bulk of the site, where content stability is a benefit, while dynamic fetching can be applied to enhance interactive user experiences, where content personalization and real-time updates are crucial. This hybrid approach allows developers to optimize their applications for both performance and flexibility, making the most of what Next.js and Sitecore offers.

References

For further information and a deeper understanding of the concepts discussed in this blog, the following resource has been referenced:

This resource provides valuable insights into the technical details and advanced usage of static and dynamic data fetching methods in Next.js and Sitecore environments.



Photo of Fishtank employee Sohrab Saboori

Sohrab Saboori

Senior Full-Stack Developer

Sohrab is a Senior Front-End Developer with extensive experience in React, Next.js, JavaScript, and TypeScript. Sohrab is committed to delivering outstanding digital solutions that not only meet but exceed clients' expectations. His expertise in building scalable and efficient web applications, responsive websites, and e-commerce platforms is unparalleled. Sohrab has a keen eye for detail and a passion for creating seamless user experiences. He is a problem-solver at heart and enjoys working with clients to find innovative solutions to their digital needs. When he's not coding, you can find him lifting weights at the gym, pounding the pavement on the run, exploring the great outdoors, or trying new restaurants and cuisines. Sohrab believes in a healthy and balanced lifestyle and finds that these activities help fuel his creativity and problem-solving skills.