DEV Community

Sebastián Aliaga
Sebastián Aliaga

Posted on

Reducing props size in Sitecore XM Cloud Next.js to improve performance

Introduction

In Sitecore XM Cloud projects using Next.js, it’s common to use multilist fields to reference other items like pages. While this offers flexibility, it can sometimes lead to components receiving a large context object when only a few fields are needed impacting performance.

In this post, I’ll walk through how I solved this by creating a plugin that trims the props down to only the necessary fields, reducing payload size and improving performance.

The Problem

We had a component that used a multilist field to reference other pages. Each selected page brought the entire context object in the GraphQL response, even though the component only used the Heading and Description.

// Example of what we were getting in props
relatedPages: [
  {
    id: '...',
    name: '...',
    fields: {
      heading: 'Example Heading',
      description: 'Example description',
      image: { /* full image object */ },
      seo: { /* SEO fields */ },
      ...many more
    }
  }
]

Enter fullscreen mode Exit fullscreen mode

The Goal

Our goal was to return only what we actually needed, not the full page context. In our case: just the heading and description fields. This would make the component faster and lighter to render.

The Solution

In headapps\sxa-starter\src\lib\page-props-factory\plugins, I created a plugin named featured-events.ts.

import { ComponentRendering, Field, LinkField } from '@sitecore-jss/sitecore-jss-nextjs';
import { SitecorePageProps } from 'lib/page-props';
import { EventDetailPage, EventDetailPageCard } from 'src/types/pages/eventDetailPage';
import { EventLocation } from 'src/types/taxonomy/eventLocation';
import { Plugin } from '..';

interface Fields {
  leadIn: Field<string>;
  heading: Field<string>;
  cta: LinkField;
  location?: EventLocation[];
  featuredEventDetailPages: FeaturedEventDetailPage[];
}

export type FeaturedEventDetailPage = {
  id: string;
  url: string;
  name: string;
  displayName: string;
  fields: EventDetailPage;
};

class FeaturedEventsPlugin implements Plugin {
  order = 1.6;

  async exec(props: SitecorePageProps) {
    props.layoutData.sitecore.route?.placeholders['main']?.forEach(
      (rendering: ComponentRendering) => {
        if (rendering.componentName === 'FeaturedEventsRow') {
          const fields = rendering.fields as unknown as Fields;
          if (fields && fields.featuredEventDetailPages) {
            fields.featuredEventDetailPages =
              fields.featuredEventDetailPages.map((page) => {
                return {
                  id: page.id,
                  url: page.url,
                  name: page.name,
                  displayName: page.displayName,
                  fields: {
                    eventName: page.fields.eventName,
                    eventDateTime: page.fields.eventDateTime,
                    eventEndDate: page.fields.eventEndDate,
                    eventAddress: page.fields.eventAddress,
                    eventTeaser: page.fields.eventTeaser,
                    eventImage: page.fields.eventImage,
                    eventLink: page.fields.eventLink,
                    eventBody: page.fields.eventBody,
                    eventLocation: page.fields.eventLocation,
                  } as EventDetailPage,
                } as EventDetailPageCard;
              }) || ([] as EventDetailPageCard[]);
            rendering.fields = fields as unknown as ComponentRendering['fields'];
          }
        }
      }
    );

    return props;
  }
}

export const featuredEventsPlugin = new FeaturedEventsPlugin();

Enter fullscreen mode Exit fullscreen mode

Results

Metric Before After
Props size ~24 KB ~4 KB
Fields included All context Only needed
Rendering speed Slower Faster

We saw reduced data sent to the client, resulting in faster hydration and better Lighthouse scores.

Other use cases

This implementation can also be used when using withDatasourceRendering on components that have as datasource the Page Context, for example, mastheads.

import { ComponentRendering } from '@sitecore-jss/sitecore-jss-nextjs';
import { SitecorePageProps } from 'lib/page-props';
import { EventDetailPage } from 'src/types/pages/eventDetailPage';
import { Plugin } from '..';

class EventDetailMastheadPlugin implements Plugin {
  order = 2.3;

  async exec(props: SitecorePageProps) {
    props.layoutData.sitecore.route?.placeholders['mastheads']?.forEach(
      (rendering: ComponentRendering) => {
        if (rendering.componentName === 'EventDetailMasthead') {
          const fields = rendering.fields as unknown as EventDetailPage;
          if (fields) {
            // Clean up the EventDetailPage fields to only include what the component uses
            const cleanedFields = {
              eventImage: fields.eventImage,
              eventDateTime: fields.eventDateTime,
              eventName: fields.eventName,
              eventLocation: fields.eventLocation,
              eventTeaser: fields.eventTeaser,
            } as EventDetailPage;

            rendering.fields = cleanedFields as unknown as ComponentRendering['fields'];
          }
        }
      }
    );

    return props;
  }
}

export const eventDetailMastheadPlugin = new EventDetailMastheadPlugin();

Enter fullscreen mode Exit fullscreen mode

Wrapping up

This is a small tweak, but it brought meaningful improvements in our XM Cloud implementation. Have you faced similar performance issues with Sitecore XM Cloud and Next.js? Let’s connect — I’d love to hear your thoughts!

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.