Insights

Edit Incoming Renderings In A Placeholder

How To Edit Incoming Renderings In A Placeholder (Next.js JSS)

If you are having issues modifying the props of a component that get rendered in a placeholder in Sitecore, this blog may be for you.

Background

Consider a parent composite component called ExpandableCardsContainer:


    const ExpandableCardsContainer = ({ rendering }: ExpandableCardsContainerProps): JSX.Element => {
      return (
        <div className="expandable-cards-container">
          <Placeholder
            name={`expandable-card`}
            rendering={rendering}
          />
        </div>
      );
    };
    export default ExpandableCardsContainer;
  

By default, you don't have to do anything to render the components inside the placeholder as JSS will do it for you. But what if you need to modify the props of the child components being rendered? For example, in my project, I had to pass in a parent component's handler function to the 'onClick' react event props of the child components.

The Solution

Utilize the render function of the Placeholder Component.

Placeholder component from @sitecore-jss provides a way to customize how your components render. This is simply achieved by adding render to the Placeholder Component, like so:


    <Placeholder
        name={`expandable-card`}
        rendering={rendering}
        render={(components: ReactNode[]) => {    // new line
          return renderCards(components);         // new line
        }}                                        // new line
      />
  

where renderCards is an arbitrary wrapper function that can modify your component.

In my case, I defined renderCards as follows:


    const renderCards = (components: any): ReactNode[] => {
      const finalComponents = [];
      for (let i = 0; i < components.length; i++) {
        if (components[i]?.type !== 'code' && components[i]?.props?.rendering) {
          finalComponents.push(transformExpandableCardComponent(components[i], i));
        } else {
          finalComponents.push(components[i]);
        }
      }
      return finalComponents;
    };
  

It is iterating through all the components and transforming them if they are not code blocks.

And finally, the transformExpandableCardComponent function, where we directly edit props is defined as:


    const transformExpandableCardComponent = (component: any, componentIndex: number) => {
      // clone
      const clonedComponent = { ...component };
      const clonedProps = { ...component.props };
  
      /* Transform Begins */
      // 1. Attach onClick
      clonedProps.onClick = () => {
        arbitraryHandleClick(componentIndex);
      };
      // 2. Toggle active state
      clonedProps.active = true;
      /* Transform Ends */
  
      // re-assemble
      clonedComponent.props = clonedProps;
      return clonedComponent;
    };
  

The reason we clone them is that by default, props in an already constructed component are edit protected, so we have no choice but to clone the entire react node. Don't worry, no information is lost during the clone and the component remains intact.

That's it! I hope this solution worked for you.

👋 Hey Sitecore Enthusiasts!

Sign up to our bi-weekly newsletter for a bite-sized curation of valuable insight from the Sitecore community.

What’s in it for you?

  • Stay up-to-date with the latest Sitecore news
  • New to Sitecore? Learn tips and tricks to help you navigate this powerful tool
  • Sitecore pro? Expand your skill set and discover troubleshooting tips
  • Browse open careers and opportunities
  • Get a chance to be featured in upcoming editions
  • Learn our secret handshake
  • And more!
Sitecore Snack a newsletter by Fishtank Consulting