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:
- Next.js Documentation on getStaticProps
- Powerful Custom Hooks for Optimizing Next.js in Sitecore XM Cloud Environments: Part 1
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.