Data Sharing Between XM Cloud Renderings With Next.js

Learn how to handle data sharing between components in your project

June 26, 2024

By John Flores

Understanding Data Sharing in Sitecore Headless Projects

In programming, we've become adept at the practice of encapsulation — a cornerstone principle where we carefully compartmentalize our code. By doing so, we make each module independent, ensuring they function autonomously within a larger system. This approach not only enhances organization but also promotes scalability and robustness in our digital architectures.

As crucial as encapsulation is, effective communication between components remains essential. In any sophisticated system, components must seamlessly interact, sharing and manipulating shared data. This exchange encourages cohesion and ensures that each element operates harmoniously within the larger framework.

Data Sharing in Sitecore Headless Projects

Similarly, when we develop our renderings in Sitecore, we usually encapsulate each component and treat them independent from one another. But there will be times where we need our components to communicate between each other. A good example for when we might need to have data shared between components are the ff:

  • Components need real-time updates, they will have to rely on a shared source of data which may have different components manipulating it, observing it and reacting to it.
  • Components show a different layout treatment depending on their parent component. They may have a single rendering which requires the same set of fields but may have a different UI on certain layouts.

These are just a few examples, and the data sharing requirement may vary depending on the application’s complexity and nature.

Learn About React Context

A simple answer to solving this problem is Context. This is a useful feature React provides us which help improve accessing information without having to manually drill in the data through props on components. There are two ways you can use React Context and help you solve problems you might be facing in your Sitecore Projects that can actually be solved using React Context.

Global Context

When we talk about Global Context, it’s a strategy of using React Context at a Layout level. This helps challenges where certain data might be needed at a site level. Some good examples of this are the following:

  • Account information - when your whole website relies on account data and returns a dynamic set of information through out the whole system.
  • Shopping cart - when certain products are added to a globally managed shopping cart.
  • Site Theme - a good example of a simple problem that might be faced by any organization which can be solved by managing the theme settings through a global context.

There are even more types of projects that deal with global data being used but the examples above a good number of examples.

Setting Up Your Context in Your JSS Next.js Project

Let’s start by creating our custom hook. We want to create a custom hook instead of adding the context right away is to separate the code from where we’re trying to add it to, in this case, it is our Layout.tsx **file. We can name our custom hook file useGlobalContext.tsx. In our little experiment we want to add a toggle between light and dark mode. The code we have below contain the types, the context, context provider and the custom hook** we are using.

type GlobalSettingsType = {
  isDarkMode: boolean;
  setIsDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
};

type GlobalSettingsProviderProps = {
  children: ReactNode;
};

const GlobalSettingsContext = createContext<GlobalSettingsType | undefined>(undefined);

export const GlobalSettingsProvider: React.FC<GlobalSettingsProviderProps> = ({ children }) => {
  const [isDarkMode, setIsDarkMode] = useState(false);

  return (
    <GlobalSettingsContext.Provider
      value={{
        isDarkMode,
        setIsDarkMode,
      }}
    >
      {children}
    </GlobalSettingsContext.Provider>
  );
};

export const useGlobalSettingsContext = () => {
  const context = useContext(GlobalSettingsContext);
  if (!context) {
    throw new Error('GlobalSettingsProvider is not found.');
  }
  return context;
};

Now that we have this in place, we can use this inside the Layout.tsx file. If you need to, you can wrap all of the three placeholders, headless-header, headless-main, and headless-footer.

<GlobalSettingsProvider>
  {/* root placeholder for the app, which we add components to using route data */}
  <div className={mainClassPageEditing}>
    <header>
      <div id="header">
        {route && <Placeholder name="headless-header" rendering={route} />}
      </div>
    </header>
    <main>
      <div id="content">
        {route && <Placeholder name="headless-main" rendering={route} />}
      </div>
    </main>
    <footer>
      <div id="footer">
        {route && <Placeholder name="headless-footer" rendering={route} />}
      </div>
    </footer>
  </div>
</GlobalSettingsProvider>

With this in place, in any of your components you can call useGlobalSettingsContext and access a shared site value that can be processed by any child component.

export const Default = (props: ComponentProps) => {
  const { isDarkMode, setIsDarkMode } = useGlobalSettingsContext();

  return (
    ...
  );
};

Components With Their Own Context

When we’re dealing with complex components, for example, we are required to make a filter that affects multiple item lists. Now these item lists are also separate components added on to the page. The difference between this and the global context is that this logic only applies to this set of components and nothing else, so globally set context doesn’t fit the need for this.

To solve this, we can create a separate component with placeholder setup. This will be a wrapper or container component that creates the context consumed by the components added inside the placeholder.

Creating Your Component With Context in Place

With the example above, you might already have an idea of how you can accomplish this task. I’ll go directly into completing the setup for the custom hook usePageListingFilter with a provider named PageListingFilterProvider which should contain a category value of the items it should only allow to be visible. Let’s create the component that will use this.

export const Default = ({ fields, rendering, params }: PageListingFilterProps) => {
  const phKey = `pagelistings-${params.DynamicPlaceholderId}`;

  return (
    <PageListingFilterProvider>
      <div>
        <Text tag="h3" className="text-neutral-100" field={fields?.heading} />
        <Placeholder name={phKey} rendering={rendering} />
      </div>
    </PageListingFilterProvider>
  );
};

Since we’ve wrapped the provider around the placeholder, we can now access the context within any component that is added under the pagelistings placeholder. The code looks pretty similar with the Global Context we created. The only difference is how and why we applied the context.

Where Do I Go From Here?

Every developer has a different set of challenges to overcome and we might not share or experience the obstacles you have for your project, but I have tried to present a general idea of how you can utilize React Context to solve a number of challenges you might experience in your development career. It might not be exactly as what you need but do some discovery and some experimenting and maybe this may be the solution you need. Check out other blogs the team has written, we have a couple of them, and you might be able to read one that will fix your problem.



John Headshot

John Flores

Front-End Developer

John is a Senior Front-End Developer who is passionate about design and development. Outside of work, John has wide range of hobbies, from his plant collection, being a dog daddy, and a foodie.