Component-level data fetching is a unique feature of Sitecore JSS with Next.js. It lets you build better-architected frontend applications by allowing for more independent modular components.
What are you going to learn from this article:
- How vanilla Next.js limits you in data fetching
- Implementing component-level data fetching in your Next.js Sitecore JSS app
- How the advanced Incremental Static Regeneration (ISR) feature works at the component level.
๐ก What do I mean by data fetching?
When I say โdata fetchingโ I mean gathering up the content to build out the page and experience that you want for your visitors. So data fetching could be making an API request to your content management system (CMS), loading images from your cloud provider, calling your personalization engine, or so many other things.
Data Fetching in Next.js WITHOUT Sitecore JSS
Taking a step back letโs first take a look at data fetching in vanilla Next.js application, meaning no Sitecore JSS. Page level data fetching is expected for vanilla Next.js applications. This approach of fetching data at a page level works, but your data fetching methods can get overwhelming quickly if you have to collect lots of data for all the components on your page.
What bugs me the most about page-level data fetching is the inability to build completely independent components. You have to rely on the parent page level for data fetching and then passing that data down to the child components. This gets even worse when you have children of children components like the animation below.
Data Fetching in Next.js with Sitecore JSS
The Sitecore JSS team has expanded the data fetching capabilities of Next.js by allowing for component-level data fetching. This means no more passing data from the parent into child components. We can fetch that data within the components ourselves, this keeps our frontends modular and follows the separation of concerns principles. See my previous blog post Modular Frontends with Component Driven Design to learn more about why this is important.
You can call the getStaticProps or getServerSide props methods within components to fetch the data you need. This means it works for both static and server-side rendered components!
For you to implement component-level data fetching itโs a little different than page level. So letโs look at those differences now and utilize component-level data fetching.
Implementing Component-Level Data Fetching
Here you can find the official Sitecore documentation on component-level data fetching. For the most part data fetching in the component is pretty similar to your pages. You create your asynchronous data fetching function, getStaticProps or getServerSideProps, and inside of that you call out to your file system, API, database, or wherever your data lives. The differences start with what you return from your data fetching method and how you access that data within your component.
GetStaticComponentProps & GetServerSideComponentProps Return types
GetStaticComponentProps
and GetServerSideComponentProps
are the two return types for the static and server-side data fetching methods. These differ from the standard page-level data fetching return types.
The biggest difference with these return types is that instead of returning a properties object you return your data object directly. Need JSON data in your component, use your data fetching method to get it and return it directly.
Static data example (๐ก Donโt forget GetStaticComponentProps
return type)
import { GetStaticComponentProps } from '@sitecore-jss/sitecore-jss-nextjs';
export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData, context) => {
const post = await fetch('MyApiEndpoint').then((res) => res.json());
return post;
};
Server-side data example (๐ก Donโt forget GetServerSideComponentProps
return type)
import { GetServerSideComponentProps} from '@sitecore-jss/sitecore-jss-nextjs';
export const getServerSideProps: GetServerSideComponentProps = async (rendering, layoutData) => {
const post = await fetch('MyApiEndpoint').then((res) => res.json());
return post;
};
Rendering UID for accessing your data
The next important piece to cover is how to access your component-level data. This is where the rendering UID comes in. Each component has a unique rendering UID that you will need to pass into the special useComponentProps
function inside of your function that exports HTML for the page.
import { useComponentProps, ComponentRendering, Field } from '@sitecore-jss/sitecore-jss-nextjs';
type ComponentData = {
rendering: ComponentRendering;
fields: {
Title: Field<string>;
SessionizeURL: Field<string>;
};
};
export const Default = (props: ComponentData): JSX.Element => {
const externalData = useComponentProps<string>(props.rendering.uid);
return (
<div>
{ externalData }
</div>
);
};
There is a decent bit of code shown above so letโs explain it a bit. First, weโll look at the ComponentData
object. The rendering
property, with the type ComponentRendering
, is the most important. That holds the UID you need.
The fields property is the data coming from Sitecore XM Cloud, the hybrid headless CMS, so itโs optional based on your use case. At a minimum, you need the ComponentRendering
property so you have the UID to access the data.
The next important piece is the call to useComponentProps
. This is a special Sitecore JSS method that fetches the data we returned from our getStaticProps or getServerSideProps methods. The <string>
in the method call indicates we expect a string to be returned. And the props.rendering.uid
is the unique component UID, which JSS uses to determine what data to return to this component.
๐ก Itโs important to note that useComponentProps can return undefined if no data is found based on the UID. So keep that in mind when accessing the externalData
variable later on in your function.
Last, inside the HTML we have access to our data and we can use it however we want. In the above case, it is a string so itโs directly being printed in the HTML.
Component-level data fetching and incremental static regeneration
A useful feature of Next.js data fetching is incremental static regeneration (ISR). This feature allows for static pages to be regenerated with new content without having to do a full rebuild. But ISR works at a page level, how do you use ISR when fetching data in the component?
โ Not familiar with Incremental Static Regeneration? Find more information in my blog: Build Times & Incremental Static Regeneration to the Rescue.
The way ISR works with component-level data fetching is that it will inherit ISR from the parent page. You do not implement ISR in the component. If the parent page that hosts your component has ISR implemented, meaning it has its revalidate
property set to some value in getStaticProps
, then your component will automatically inherit that and update in the same time interval.
This does mean ISR will regenerate components with new data, which is great, but it is not very flexible. Let's say you didnโt want or need ISR for your child component, unfortunately, there is no way to turn ISR off in your component. This isnโt the end of the world but could mean unnecessary API calls are made.
Summary
The ability to perform component-level data fetching in Next.js with Sitecore JSS allows you to put your data fetching calls exactly where you need them instead of at a page level and passing your data around. This helps to keep your components more independent and modular.
Component-level data fetching even works with ISR so you reduce build times and keep your page content up to date.
This is a great feature of Sitecore JSS and helps it stand out as a headless development tool. Special thanks to Adam Brauer and the whole Sitecore JSS team for help with implementing this feature on my own app and for sharing details on how ISR works inside your components.
Find the official Sitecore documentation on component-level data fetching here: Component-Level Data Fetching Documentation
Find more information about Sitecore JSS and the Headless Services here: Sitecore Developer Portal Headless Services
Top comments (1)
Nice explanation and example, but one obvious question is, how do we pass any parameters to the Get..Props functions to control what we are passing to the API call?
for example, we might want to ask for a url with record id so calling myapi.com/api/getRecord?recId=X
So how do we pass in a value for X? or if this isn't possible, how do we get the query params inline from the current URL? I tried
const searchParams = useSearchParams();
const params = searchParams.get('params'); // e.g. 123
but the GetStaticComponentProps function blows up!