<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Gutierrez</title>
    <description>The latest articles on DEV Community by Juan Gutierrez (@juan_dvd).</description>
    <link>https://dev.to/juan_dvd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2166267%2Fb3bac053-1b8b-4d69-9d2f-24cd7886cb8d.jpeg</url>
      <title>DEV Community: Juan Gutierrez</title>
      <link>https://dev.to/juan_dvd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/juan_dvd"/>
    <language>en</language>
    <item>
      <title>Enhancing Multisite and Market-Specific Handling with Sitecore JSS and Next.js</title>
      <dc:creator>Juan Gutierrez</dc:creator>
      <pubDate>Fri, 04 Oct 2024 10:47:14 +0000</pubDate>
      <link>https://dev.to/juan_dvd/enhancing-multisite-and-market-specific-handling-with-sitecore-jss-and-nextjs-26n8</link>
      <guid>https://dev.to/juan_dvd/enhancing-multisite-and-market-specific-handling-with-sitecore-jss-and-nextjs-26n8</guid>
      <description>&lt;p&gt;When building a multisite, multilingual website with Sitecore JSS and Next.js, efficiently managing market-specific content for both editors and users presents unique challenges. While Sitecore provides powerful features for handling multiple languages and sites out of the box, extending these capabilities for a multi-market setup requires custom implementation to ensure a seamless experience.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore how Sitecore’s default multisite and multilingual handling works, and how we built on top of it to meet Uniworld's market-specific requirements. We'll cover the key plugins and configurations we modified, and the rationale behind each change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Sitecore Multisite and Language Handling
&lt;/h3&gt;

&lt;p&gt;Sitecore's Next.js Multisite Add-on includes a rich set of features designed to simplify multisite and multilingual setups:&lt;/p&gt;

&lt;p&gt;Multisite Middleware: Rewrites requests to the correct site based on the incoming hostname.&lt;br&gt;
Site Resolver: Uses GraphQL to fetch site information at build time, including site-specific settings like default language.&lt;br&gt;
Internationalized Routing: Uses Next.js i18n to manage different languages based on the request path, with fallbacks to default settings when necessary.&lt;br&gt;
By default, Sitecore handles language and site-specific routing using a pattern like /&lt;em&gt;site&lt;/em&gt;/. This ensures that content is appropriately scoped across different sites hosted under the same Next.js instance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sitecore Default Language Behavior
&lt;/h3&gt;

&lt;p&gt;In the default setup, Sitecore and Next.js use the hostname and URL path to determine which site and language to serve. If a language prefix is available in the URL (e.g., /en-us), Sitecore serves content in that language. If the language is not specified, it uses the default language for the given site.&lt;/p&gt;

&lt;p&gt;However, this default behavior does not seamlessly accommodate market-specific redirections, requiring customizations to handle:&lt;/p&gt;

&lt;p&gt;Serving market-specific content based on the user's country or preferences.&lt;br&gt;
Supporting the Experience Editor and Preview without conflicts.&lt;br&gt;
Integrating market handling into the multisite setup.&lt;/p&gt;
&lt;h3&gt;
  
  
  Challenges in Multisite and Market Handling
&lt;/h3&gt;

&lt;p&gt;We faced a few challenges while creating a multi-market solution:&lt;/p&gt;

&lt;p&gt;Serving Correct Content by Market: Ensuring the correct market was targeted based on the user's location or preferences.&lt;br&gt;
Experience Editor Compatibility: Ensuring that editors could preview and edit content without inappropriate redirections.&lt;br&gt;
Scalable Multisite Integration: Seamlessly integrating market-specific handling with Sitecore’s multisite capabilities.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mapping Languages and Markets
&lt;/h3&gt;

&lt;p&gt;Our goal was to create a multi-market experience that delivers localized content tailored to users' geographic locations or preferences. Here is how we mapped markets and corresponding locales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asia-Pacific (AP): en-PH for English spoken in Asia.&lt;/li&gt;
&lt;li&gt;Australia (AU): en-AU.&lt;/li&gt;
&lt;li&gt;Canada (CA): en-CA.&lt;/li&gt;
&lt;li&gt;European Union (EU): en-IE.&lt;/li&gt;
&lt;li&gt;New Zealand (NZ): en-NZ.&lt;/li&gt;
&lt;li&gt;United Kingdom (UK): en-GB.&lt;/li&gt;
&lt;li&gt;United States (US): en-US.&lt;/li&gt;
&lt;li&gt;South Africa (ZA): en-ZA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective was to ensure that users from these markets receive localized content for their specific market, and we wanted to handle this efficiently at the middleware level using Next.js.&lt;/p&gt;
&lt;h3&gt;
  
  
  Custom Implementation: Steps Taken
&lt;/h3&gt;

&lt;p&gt;To meet these requirements, we introduced several key changes in Next.js middleware and configuration, as well as Sitecore plugins. Here is a detailed breakdown of the updates.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Market Plugin for Middleware
&lt;/h4&gt;

&lt;p&gt;The Market Plugin addresses market-specific content handling by leveraging middleware in Next.js. It ensures users are redirected to the appropriate market URL, with proper locale detection and handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class marketPlugin implements MiddlewarePlugin {
  order = 0;

  constructor() {
    console.log('[marketPlugin] Initializing marketPlugin...');
  }
  async exec(req: NextRequest, res?: NextResponse): Promise {
    const { pathname } = req.nextUrl;

    // Exclude assets and other unnecessary routes
    if (this.excludeRoute(pathname)) {
      console.log([marketPlugin] Skipping route: ${pathname});
      return res || NextResponse.next();
    }

    console.log([marketPlugin] Executing marketPlugin for request: ${req.url});

    const cookieLocale = req.cookies.get('NEXT_LOCALE');
    const country = req.geo?.country?.toLowerCase() || 'us';

    let market: string;
    let locale: string;

    const urlmarket = pathname.split('/')[1];

    if (validmarkets.includes(urlmarket)) {
      console.log([marketPlugin] URL market detected: ${urlmarket});
      market = urlmarket;
    } else if (cookieLocale) {
      console.log([marketPlugin] Locale cookie found: ${cookieLocale.value});
      market = localeTomarketMap[cookieLocale.value.toLowerCase()] || 'us';
    } else {
      console.log([marketPlugin] No valid market found, using country: ${country});
      market = determinemarketFromCountry(country);
    }

    locale = determineLanguageFrommarket(market);
    console.log([marketPlugin] Determined locale from market: ${locale});

    if (cookieLocale?.value !== locale) {
      console.log([marketPlugin] Setting locale cookie to: ${locale});
      res = res || NextResponse.next();
      res.cookies.set('NEXT_LOCALE', locale, {
        path: '/',
        maxAge: 60 * 60 * 24 * 30, // 30 days
      });
    }

    if (!pathname.startsWith(/${market})) {
      const newUrl = req.nextUrl.clone();
      newUrl.pathname = /${market}${pathname === '/' ? '' : pathname};
      console.log([marketPlugin] Redirecting to market-specific path: ${newUrl});
      res = NextResponse.redirect(newUrl);
    }

    console.log([marketPlugin] Finished marketPlugin execution for request: ${req.url});
    return res || NextResponse.next();
  }

  private excludeRoute(pathname: string): boolean {
    const isExcluded =
      pathname.includes('.') || // Skip files (e.g., .css, .js, .png)
      pathname.startsWith('/api/') || // Skip Next.js API routes
      pathname.startsWith('/sitecore/'); // Skip Sitecore API routes

    console.log([marketPlugin] Checking if route should be excluded: ${pathname} - Excluded: ${isExcluded});
    return isExcluded;
  }
}
export const marketPlugin = new marketPlugin();  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why We Modified It:&lt;br&gt;
Path Detection and Exclusion: The excludeRoute function was implemented to skip unnecessary routes like assets, API requests, and Sitecore service requests.&lt;br&gt;
Market Detection: The plugin first checks if a valid market prefix exists in the URL. If not, it attempts to use a cookie (NEXT_LOCALE) to determine the market. If the cookie is unavailable, it falls back to the country code from the request geo headers.&lt;br&gt;
Setting Cookies and Redirects: If the market in the URL does not match the detected or preferred market, the user is redirected to the appropriate market path, and the locale cookie is set for consistent behavior.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Updated next.config.js for Market and Locale Management
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const jssConfig = require('./src/temp/config');

module.exports = {
  i18n: {
    locales: [
      'en', 'en-PH', 'en-AU', 'en-CA', 'en-IE', 'en-NZ', 'en-ZA', 'en-GB', 'en-US',
    ],
    defaultLocale: jssConfig.defaultLanguage,
    localeDetection: false, // Disabled locale detection since we handle it through middleware
  },
}; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why We Modified It:&lt;br&gt;
We disabled automatic locale detection (localeDetection: false) because our custom middleware handled the logic for determining the correct locale and market. This allowed us more granular control over which market a user should see.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Update to page-props.ts to Include Market
&lt;/h4&gt;

&lt;p&gt;To support the new changes, the SitecorePageProps interface was updated to include the market property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type SitecorePageProps = {
  site: SiteInfo;
  locale: string;
  dictionary: DictionaryPhrases;
  componentProps: ComponentPropsCollection;
  notFound: boolean;
  layoutData: LayoutServiceData;
  headLinks: HTMLLink[];
  market: string; // New property for storing market information
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key Changes:&lt;br&gt;
Market Property: Added market to store the market context for each request.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Updated Page Props Factory Plugin
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SitePlugin implements Plugin {
  order = 0;
  async exec(props: SitecorePageProps, context: GetServerSidePropsContext | GetStaticPropsContext) {
    if (context.preview) {
      console.log('[SitePlugin] Preview mode detected. Skipping site resolution.');
      return props;
    }
    const path = this.getNormalizedPath(context);
    console.log([SitePlugin] Resolving site for path: ${path});

    const siteData = getSiteRewriteData(path, config.sitecoreSiteName);
    console.log([SitePlugin] Site data extracted from path:, siteData);

    const market = this.getmarketFromParams(context);
    const locale = determineLanguageFrommarket(market);

    context.locale = locale;
    props.site = siteResolver.getByName(siteData.siteName);
    props.locale = locale;
    props.market = market;
    console.log([SitePlugin] Props:, props);
    console.log([SitePlugin] Context:, context);

    return props;
  }

  private getmarketFromParams(context?: GetServerSidePropsContext | GetStaticPropsContext): string {
    if (context?.params &amp;amp;&amp;amp; Array.isArray(context.params.path) &amp;amp;&amp;amp; context.params.path.length &amp;gt; 1) {
      const [, secondSegment] = context.params.path;
      if (validmarkets.includes(secondSegment)) {
        context.params.path.splice(1, 1);
        return secondSegment;
      }
    }
    return '';
  }

  private getNormalizedPath(context: GetServerSidePropsContext | GetStaticPropsContext): string {
    if (!context.params) return '/';
    return Array.isArray(context.params.path) ? context.params.path.join('/') : context.params.path ?? '/';
  }
}
export const sitePlugin = new SitePlugin();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why We Modified It:&lt;br&gt;
The SitePlugin helps resolve the current site context based on the request, and modifications were made to include both market and locale. This ensures that the rest of the page-building process is aware of the market-specific context.&lt;/p&gt;

&lt;p&gt;Key Changes:&lt;br&gt;
Market and Locale Handling: The SitePlugin was updated to extract market information from the URL and set it within the context, ensuring consistency across the rendering process.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Preview Mode Plugin Updates
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { GetServerSidePropsContext, GetStaticPropsContext } from 'next';
import {
  SiteInfo,
  personalizeLayout,
  getGroomedVariantIds,
} from '@sitecore-jss/sitecore-jss-nextjs';
import {
  editingDataService,
  isEditingMetadataPreviewData,
} from '@sitecore-jss/sitecore-jss-nextjs/editing';
import { SitecorePageProps } from 'lib/page-props';
import { graphQLEditingService } from 'lib/graphql-editing-service';
import { Plugin } from '..';
import { determinemarketFromLocale } from 'lib/helpers/marketHelper';

class PreviewModePlugin implements Plugin {
  order = 1;

  async exec(props: SitecorePageProps, context: GetServerSidePropsContext | GetStaticPropsContext) {
    if (!context.preview) {
      console.log('[PreviewModePlugin] Not in preview mode. Skipping preview logic.');
      return props;
    }
    console.log('[PreviewModePlugin] Preview mode detected.');
    // If we're in Pages preview (editing) Metadata Edit Mode, prefetch the editing data
    if (isEditingMetadataPreviewData(context.previewData)) {
      console.log('[PreviewModePlugin] Detected Pages preview in Metadata Edit Mode.');

      const { site, itemId, language, version, variantIds, layoutKind } = context.previewData;
      console.log([PreviewModePlugin] Preview data received:, context.previewData);

      try {
        const data = await graphQLEditingService.fetchEditingData({
          siteName: site,
          itemId,
          language,
          version,
          layoutKind,
        });

        if (!data) {
          throw new Error(
            Unable to fetch editing data for preview ${JSON.stringify(context.previewData)}
          );
        }

        console.log('[PreviewModePlugin] Editing data successfully fetched:', data);

        const locale = context.previewData.language;
        const market = determinemarketFromLocale(locale);
        props.site = data.layoutData.sitecore.context.site as SiteInfo;
        props.layoutData = data.layoutData;
        props.dictionary = data.dictionary;
        props.headLinks = [];
        props.locale = locale;
        props.market = market;
        console.log([PreviewModePlugin] Props:, props);
        console.log([PreviewModePlugin] Context:, context);
        const personalizeData = getGroomedVariantIds(variantIds);
        console.log('[PreviewModePlugin] Groomed variant IDs:', personalizeData);

        personalizeLayout(
          props.layoutData,
          personalizeData.variantId,
          personalizeData.componentVariantIds
        );

        console.log('[PreviewModePlugin] Personalized layout data applied.');
      } catch (error) {
        console.error('[PreviewModePlugin] Error fetching editing data:', error);
        throw error;
      }

      return props;
    }

    // If we're in preview (editing) Chromes Edit Mode, use data already sent along with the editing request
    console.log(
      '[PreviewModePlugin] Detected preview mode using Experience Editor (Chromes Edit Mode).'
    );

    try {
      const data = await editingDataService.getEditingData(context.previewData);
      if (!data) {
        throw new Error(
          Unable to get editing data for preview ${JSON.stringify(context.previewData)}
        );
      }

      console.log(
        '[PreviewModePlugin] Editing data successfully retrieved from Experience Editor:',
        data
      );
      const locale = data.language;
      const market = determinemarketFromLocale(locale);
      props.site = data.layoutData.sitecore.context.site as SiteInfo;
      props.layoutData = data.layoutData;
      props.dictionary = data.dictionary;
      props.headLinks = [];
      props.locale = locale;
      props.market = market;
      console.log([PreviewModePlugin] Props:, props);
      console.log([PreviewModePlugin] Context:, context);
    } catch (error) {
      console.error('[PreviewModePlugin] Error getting editing data:', error);
      throw error;
    }

    return props;
  }
}

export const previewModePlugin = new PreviewModePlugin();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why We Modified It:&lt;br&gt;
We modified the PreviewModePlugin to set market-specific information during the preview process. This ensured that editors using the Experience Editor would see the correct variant of the content corresponding to the specified market.&lt;/p&gt;

&lt;p&gt;Key Changes:&lt;br&gt;
Personalization Handling: The plugin handles market and locale during preview to provide editors with a preview that is consistent with what end users will experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;Setting up a multisite and multilingual website for Uniworld with Sitecore XM Cloud and Next.js involved overcoming challenges around market-specific content targeting and editor experience. Here’s a summary of the key steps taken:&lt;/p&gt;

&lt;p&gt;MarketPlugin Middleware: Handled market path redirection and cookie management to ensure users were served the correct content.&lt;br&gt;
Updated next.config.js: Configured locales, rewrites, and disabled locale detection for more control.&lt;br&gt;
Page Props Plugins: Updated site.ts and previewMode.ts to properly handle market and locale information, making it consistent across different page states.&lt;br&gt;
Updated Page Props: Added the market context to SitecorePageProps for easy access.&lt;br&gt;
This custom solution allows Sitecore XM Cloud to deliver the right content to the right users, ensuring an optimal experience for both visitors and content editors. It is modular and easily extendable, making it a strong foundation for future growth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.asuinnovation.com/blog/sitecore-xm-middleware-multi-market/" rel="noopener noreferrer"&gt;https://www.asuinnovation.com/blog/sitecore-xm-middleware-multi-market/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blogs.perficient.com/2023/12/08/a-full-guide-to-creating-a-multi-language-sites-with-sitecore-xm-cloud-and-next-js/" rel="noopener noreferrer"&gt;A Full Guide to Creating Multi-Language Sites with Sitecore XM Cloud and Next.js&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.sitecore.com/xmc/en/developers/jss/latest/jss-xmc/internationalization-in-the-jss-sample-app-for-next-js.html" rel="noopener noreferrer"&gt;Internationalization in the JSS Sample App for Next.js&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.getfishtank.com/blog/guide-to-implementing-multilingual-support-in-sitecore-xm-cloud" rel="noopener noreferrer"&gt;Guide to Implementing Multilingual Support in Sitecore XM Cloud&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.sitecore.com/xmc/en/developers/jss/latest/jss-xmc/the-next-js-multisite-add-on.html" rel="noopener noreferrer"&gt;The Next.js Multisite Add-On&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miguelminoldo.fr/2022/07/22/multisite-support-sitecore-next-edge-middleware/" rel="noopener noreferrer"&gt;Multisite Support with Sitecore Next.js and Edge Middleware&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to solve component refresh in Sitecore XM Cloud using Higher-Order Components</title>
      <dc:creator>Juan Gutierrez</dc:creator>
      <pubDate>Fri, 04 Oct 2024 10:32:22 +0000</pubDate>
      <link>https://dev.to/juan_dvd/how-to-solve-component-refresh-in-sitecore-xm-cloud-using-higher-order-components-2lan</link>
      <guid>https://dev.to/juan_dvd/how-to-solve-component-refresh-in-sitecore-xm-cloud-using-higher-order-components-2lan</guid>
      <description>&lt;p&gt;When working with Sitecore XM Cloud, combined with tools like Chakra UI, developers may encounter issues related to component refreshing within Sitecore Pages. One major inconvenience is that some components do not refresh automatically when changes are made, which can be frustrating, especially when aiming for a smooth workflow that allows real-time editing.&lt;/p&gt;

&lt;p&gt;To address this issue, we reached out to Sitecore support and engaged with the Sitecore Slack community, where Jeff L'Heureux and David Ly kindly offered a solution. In this article, we will explore the problem, describe the proposed solution, and provide a step-by-step guide to enhance the editing experience in Sitecore Pages.&lt;/p&gt;

&lt;p&gt;Best Practices: Component Class Name Requirements&lt;/p&gt;

&lt;p&gt;According to Sitecore best practices, every component should include a class name to support the advanced editing features of XM Cloud. Class names are passed through rendering parameters, and these must be applied to the components. Creating New Components&lt;/p&gt;

&lt;p&gt;The following structure demonstrates how to incorporate class names in a compliant manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className="{component" promo ${props.params.styles}} id="{id" ? id : undefined}&amp;gt;      
   &amp;lt;p&amp;gt;Component Markup Here&amp;lt;/p&amp;gt; 
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in our project, we are using Chakra UI for component styling. Chakra UI offers a highly modular and CSS-in-JS-based styling approach that provides greater control over our styles, eliminating the need to manually manage CSS class names. This reduces the risk of conflicts and makes our code cleaner and more maintainable.&lt;/p&gt;

&lt;p&gt;In our case, we do not want to override the className attribute because it would break the structure and integration provided by Chakra UI, which relies on props to dynamically apply styles, such as Box, Flex, and other Chakra components. Instead of overriding or manually managing class names, we utilize Chakra’s prop-based styling for layout and design, which helps in maintaining consistency and flexibility across the project.&lt;/p&gt;

&lt;p&gt;Thus, while adhering to the spirit of Sitecore’s best practices, we choose not to apply the className attribute directly. Instead, we use a customized solution to ensure that the core functionalities required by Sitecore, including rendering parameters and editing capabilities, are preserved without compromising on the styling approach provided by Chakra UI&lt;/p&gt;

&lt;p&gt;The Problem&lt;br&gt;
The main issue arises when components do not refresh correctly in Sitecore Pages while using Chakra UI to manage styles. This limitation affects real-time changes while editing the page, which can be quite disruptive. This happens even in a native Sitecore implementation, where some containers and other components do not update as expected. To solve this, we needed a reliable way to force the refresh of these components.&lt;/p&gt;

&lt;p&gt;Here is an example of the issue before applying the solution:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/33Za0rp1RFw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Objective&lt;/p&gt;

&lt;p&gt;Our objective is to implement a solution that forces React components to update when their parameters or styles change, ensuring that the changes are reflected automatically in Sitecore Pages without requiring manual adjustments.&lt;/p&gt;

&lt;p&gt;Solution: Use a Higher-Order Component (HOC) to Force Updates&lt;/p&gt;

&lt;p&gt;To solve this problem, we use a Higher-Order Component (HOC), a common pattern in React that allows extending a component's functionality without directly modifying its code. In this case, we create an HOC that monitors changes in the component's styles and forces an update.&lt;/p&gt;

&lt;p&gt;Step-by-Step to Implement the Solution&lt;/p&gt;

&lt;p&gt;1.Create the HOC to Detect Style Changes&lt;/p&gt;

&lt;p&gt;The first step is to create a component called paramsWatcher. This component is responsible for wrapping any component to which we want to apply the refresh logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Author: David Ly
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
import { ComponentProps } from 'lib/component-props';
import { useRef, useState, useEffect } from 'react';

export function paramsWatcher&amp;lt;P extends ComponentProps&amp;gt;(
  WrappedComponent: React.ComponentType&amp;lt;P&amp;gt;
) {
  function WatcherComponent(props: P) {
    const ref = useRef&amp;lt;HTMLDivElement&amp;gt;(null);
    const [styles, setStyles] = useState(props.params.Styles);

    const context = useSitecoreContext();
    const isEditing = context?.sitecoreContext?.pageEditing;

    useEffect(() =&amp;gt; {
      if (!ref.current || !isEditing) {
        return;
      }
      console.log('Configurando el observador de mutaciones...');
      const observer = new MutationObserver((mutations) =&amp;gt; {
        mutations.forEach((mutation) =&amp;gt; {
          if (mutation.type === 'attributes' &amp;amp;&amp;amp; mutation.attributeName === 'class') {
            const [, ...classes] = ref.current?.classList.value.split(' ') ?? [];
            console.log('Cambio detectado en las clases:', classes);
            setStyles(classes.join(' '));
          }
        });
      });
      observer.observe(ref.current, { attributes: true });
      return () =&amp;gt; {
        console.log('Desconectando el observador de mutaciones...');
        observer.disconnect();
      };
    }, [isEditing, props.params]);

    if (!isEditing) {
      return &amp;lt;WrappedComponent {...props} /&amp;gt;;
    }
    // Actualizar los estilos en los parámetros antes de renderizar
    props.params.Styles = styles;
    return (
      &amp;lt;&amp;gt;
        &amp;lt;div ref={ref} className={'component ' + styles} style={{ display: 'none' }} /&amp;gt;
        &amp;lt;WrappedComponent {...props} /&amp;gt;
      &amp;lt;/&amp;gt;
    );
  }
  return WatcherComponent;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Wrap the Component We Want to Refresh
Now that we have the HOC, we can use it to wrap our HeroComponent, which has issues refreshing in Sitecore Pages.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Image as JssImage, Text as JssText } from '@sitecore-jss/sitecore-jss-nextjs';
import { Box, Text, Flex } from '@chakra-ui/react';
import {
  AlignmentStyle,
  ColorStyle,
  HeroModel,
  UpdateHeroParams,
} from 'lib/classes/Components/Hero/HeroLib';
import { withPagesStyleChangeWatcher } from 'src/util/withPagesStyleChangeWatcher';

const HeroDefaultComponent = (): JSX.Element =&amp;gt; (
  &amp;lt;Box textAlign="center"&amp;gt;
    &amp;lt;Text fontSize="xl"&amp;gt;No hay contenido disponible. Asigne una fuente de datos.&amp;lt;/Text&amp;gt;
  &amp;lt;/Box&amp;gt;
);

const HeroComponent = (
  model: HeroModel &amp;amp; { heroStyles?: { Alignment: AlignmentStyle; Color: ColorStyle } }
): JSX.Element =&amp;gt; {
  const params =
    model.heroStyles &amp;amp;&amp;amp; model.heroStyles.Alignment &amp;amp;&amp;amp; model.heroStyles.Color
      ? model.heroStyles
      : UpdateHeroParams(model);

  if (model.fields &amp;amp;&amp;amp; model.fields.Image &amp;amp;&amp;amp; model.fields.Title) {
    return (
        &amp;lt;Box position="relative"&amp;gt;
          &amp;lt;JssImage field={model.fields.Image} /&amp;gt;
          &amp;lt;Flex
            color={{ base: 'uniblue', md: params.Color.textColor }}
            position={{ base: 'static', md: 'absolute' }}
            height="100%"
            width="100%"
            top="0"
            flexDirection="column"
            justifyContent="center"
          &amp;gt;
            &amp;lt;Box
              ml={params.Alignment.marginLeft}
              mr={params.Alignment.marginRight}
              maxW={{ base: '100%', md: '50%' }}
              textAlign="center"
            &amp;gt;
              &amp;lt;Text variant="h1" mb={0}&amp;gt;
                &amp;lt;JssText field={model.fields.Title} /&amp;gt;
              &amp;lt;/Text&amp;gt;
              &amp;lt;Text variant="subtitle"&amp;gt;
                &amp;lt;JssText field={model.fields.Description} /&amp;gt;
              &amp;lt;/Text&amp;gt;
            &amp;lt;/Box&amp;gt;
          &amp;lt;/Flex&amp;gt;
        &amp;lt;/Box&amp;gt;
    );
  }
  return &amp;lt;HeroDefaultComponent /&amp;gt;;
};
export default paramsWatcher(HeroComponent); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Testing the Solution in Sitecore Pages
Once the HOC was implemented and the component wrapped, we tested the solution in Sitecore XM Cloud Pages. Below, you can see the result after applying the solution:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TcxPzCYpsMs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
The solution presented uses a Higher-Order Component to observe changes in the component's styles and force a refresh when necessary. By combining the power of React with observing changes in the DOM using MutationObserver, we can address the issue of components that typically do not update automatically in Sitecore.&lt;/p&gt;

&lt;p&gt;While this is an effective solution, it is important to note that further research is required for certain types of components (like containers), and it depends on how Sitecore manages refresh in Pages. React provides a powerful tool to handle these cases, but it's essential to test and adjust accordingly.&lt;/p&gt;

&lt;p&gt;We would like to thank Jeff L'Heureux and David Ly for his support and the Sitecore Slack community for helping us find a viable solution to this problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.asuinnovation.com/blog/sitecore-xm-pages-hoc-for-component-refresh/" rel="noopener noreferrer"&gt;https://www.asuinnovation.com/blog/sitecore-xm-pages-hoc-for-component-refresh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.sitecore.com/learn/accelerate/xm-cloud/implementation/developer-experience/creating-new-components" rel="noopener noreferrer"&gt;Creating New Components&lt;/a&gt;&lt;br&gt;
&lt;a href="https://legacy.reactjs.org/docs/higher-order-components.html" rel="noopener noreferrer"&gt;Higher-Order Components&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
