Insights

Next Dynamic for Sitecore XM Cloud Development

Avoid using Next/Dynamic for your Sitecore authorable components

Troubleshooting Component Loading in Sitecore XM Cloud

Experience Editor wasn't loading my components on the QA server, but everything was loading fine on my local machine (how typical). I needed to figure out what was causing the issue and how to solve it.

Tech Specs: @sitecore-jss/sitecore-jss-nextjsv21.5.0

Sitecore Product: XM Cloudv1.5.278

Debugging Next Dynamic and Sitecore

Recently, we were building out a multistep custom form for a client using XM Cloud that required the entire form to be editable inside Sitecore Experience Editor. While architecting out the solution for our humongous form, I decided the most optimal solution would be to dynamically import (lazy load) each step of the form only when the user needs it. Since the form had some steps that users would never see, it seemed almost obvious that this was the way forward.

I created Dynamic imports for each part of my form, and set a sitecoreContext.pageEditing flag on each step, which would allow the client to author each page inside of Experience Editor.

Code snippet showing conditional rendering of dynamic components in Sitecore JSS with text saying "this is a dynamically imported component. Since it is locked behind a conditional render, it will be lazy loaded once it's required"

While developing on my local machine, I had no issues using the form on the front end or editing values inside the Experience Editor. However, once we had finished developing the form and pushed it to our QA server, none of the subcomponents were loading inside the Experience Editor.

After some digging, adding in some cheesy console.log's to test what was/wasn't loading, I realized that perhaps next/dynamic was causing issues with Sitecore and XM Cloud. After converting the dynamic imports back to traditional imports - everything loaded perfectly inside of Experience Editor!

When Can I Use Next/Dynamic With Sitecore/XM Cloud?

There doesn't seem to be much (or any) documentation about using Next/Dynamic with Sitecore, which is upsetting because it's an important way to code split your Next.js solution. Luckily, I've only found this issue happening when I'm trying to conditionally render top level components that need to be edited in Experience Editor. I'm going to go through a few common use cases for Next/Dynamic and highlight where I have yet to find any issues. This way we can still get some code splitting benefits in our Sitecore JSS projects!

How to Use Next Dynamic With Sitecore

There is a new way to lazy load components that easily documented, and its actually even easier to implement than the traditional way of importing dynamic in your components. Simply apply the suffix .dynamic to your file name, and the componentBuilder will auto-detect and dynamically load your components for you!

List of TypeScript files for step-based dynamic wrappers in a software project

So when a component file is created (ex: MyComponent.dynamic.tsx) it will be loaded as dynamic() via src/temp/componentBuilder.ts. This is how it’s imported in componentBuilder.ts:

const GraphQLConnectedDemo = {
  module: () => import('src/components/graphql/GraphQL-ConnectedDemo.dynamic'),
  element: (isEditing?: boolean) => isEditing ? require('src/components/graphql/GraphQL-ConnectedDemo.dynamic')?.default : dynamic(GraphQLConnectedDemo.module)
}

If you want even more context, I found the original Pull Request that implemented this feature. [Next.js] Enable dynamic component import in sample by illiakovalenko · Pull Request #727 · Sitecore/jss

Lazy Loading Components → Avoid

Needless to say, this is where I was having all my problems: Lazy Loading a component that is directly used in the parent. The key areas I've found this failing is when the parent is directly linked to a Sitecore rendering item, and we are doing a full lazy load import (like in the image example above). To prevent Lazy Loading using Next/Dynamic, you will want to avoid using conditional renders for any components imported using dynamic().

Code Example:

import dynamic from 'next/dynamic';
const CustomSitecoreComponent = dynamic(
  () => import('child-component/CustomSitecoreComponent')
);

...

{/* AVOID CONDITIONAL RENDERS LIKE: */}
{!isDisplayCustomComponent && (
  <div className={containerStyles}>
    <CustomSitecoreComponent fields={props.fields} />
  </div>
)}

{/* ALSO AVOID TURNARIES LIKE: */}
{!isDisplayCustomComponent ? (
  <div className={containerStyles}>
    <CustomSitecoreComponent fields={props.fields} />
  </div>
) : (
    <></>
)}

Note: If your eagle- eye noticed I am importing from a /child-component/ folder instead of the normal /component/ folder and want to know why, give this blog post a read that explains the importance of splitting out your components.

Lazy Loading Non-Experience Editor Components → Utilize

Since all the problems come from Authoring inside Experience Editor, it should be obvious that lazy loading components that don't have any Sitecore editable fields (only hard-coded or API returned values) won't cause any problems. In case you're slightly confused what the difference is, here is a code example:

Code Example:

{/* Skeleton to display while loading the CreditCard data */}
{!CardData && (
  <div className="h-[180px] w-[247px] md:h-[234px] md:w-[352px]">
    <Skeleton count={1} height={180} />
  </div>
)}

{/*  Conditionally render the users Credit Card */}
{!isCreditCardSelect && CardData != null && (
  <div className={containerStyles}>
      //CardData is returned by an API call, there are 0 authorable fields 
      //inside this component -> This allows us to dynamically import it!
    <CreditCardDisplay CardData={CardData} />
  </div>
)}

Disabling SSR → Utilize

I've had 0 issues using Next/Dynamic to disable Server Side Rendering on my Sitecore JSS components. All the issues come from lazy loading, so as long as you aren't hiding your component behind a conditional render, this will be fine!

Code Example:

import dynamic from 'next/dynamic';
const CustomSitecoreComponent = dynamic(
  () => import('child-component/CustomSitecoreComponent'), 
  { ssr: false } //Adding the "ssr: false" flag on the import
);

...

{/* THIS SHOULD WORK PERFECTLY FINE */}
<CustomSitecoreComponent fields={props.fields} />

{/* THIS HAS A HIGH CHANCE OF CAUSING AN ISSUE: */}
{!isDisplayCustomComponent && (
  <div className={containerStyles}>
    <CustomSitecoreComponent fields={props.fields} />
  </div>
)}

Final Thoughts on Sitecore and Next Dynamic

As of writing, it's very upsetting that I'm unable to use Next/Dynamic to code split my Sitecore JSS front end; however, we are still able to use Next/Dynamic in niche use cases, so at least we still have some small benefits. I've submitted a Sitecore support ticket informing them about this issue, so hopefully they will fix/implement the ability to utilize this feature soon!



Meet Tyler Holmes

Full Stack Developer

🇬🇧🧑‍💻🏸

Tyler is an experienced Full Stack Developer who has worked on a plethora of unique projects. He possesses a deep understanding of Technical SEO, C#, and React/Next.js.

Connect with Tyler