Let custom components react instantly to style changes in Page builder

In Page builder Editor mode, when a user changes the styles of a component in the right-hand styling panel, seeing the style changes in the canvas sometimes requires a page reload, which can be disruptive.

To avoid this, you can make a component subscribe to an event containing:

  • instanceId - That identifies the component instance on the canvas.
  • cssClasses - The updated CSS class string.

For a component to subscribe to the event, add the following code to your component:

import { useEffect, useState } from 'react';

function useEventDrivenStyles(id: string, initialStyles: string): string {
  const [eventStyles, setEventStyles] = useState(initialStyles);

  useEffect(() => {
    const handleStylesChanged = (event: Event) => {
      const { instanceId, cssClasses } = (event as CustomEvent).detail;

      if (instanceId !== id) {
        return;
      }

      setEventStyles(cssClasses ?? '');
    };

    window.addEventListener('sitecore:component-style-change', handleStylesChanged);

    return () => {
      window.removeEventListener('sitecore:component-style-change', handleStylesChanged);
    };
  }, [id]);

  return eventStyles;
}

The solution is event-driven, non-breaking, and opt-in per component. Components that need style-driven runtime recalculation can subscribe and react without forcing full canvas reloads. Make sure you use the exact browser event name dispatched by the platform sitecore:component-style-change.

For example:

const renderingInstanceId = props.rendering.uid;
const eventStyles = useEventDrivenStyles(renderingInstanceId, props.params.styles);
const marginTop = eventStyles.includes('container-color-background') ? 4 : 0;

return (
  <ComponentContent styles={props.params?.Styles} id={props.params?.RenderingIdentifier}>
    <>
      <div>
        <Text field={{ value: 'First paragraph' }} />
      </div>
      <div data-class-change className={`mt-${marginTop} text-muted`}>
        <Text field={{ value: 'This text has a top margin of 4 (1rem).' }} />
      </div>
    </>
  </ComponentContent>
);
If you have suggestions for improving this article, let us know!