My new blog present here.

Featured Post

Insights into Sitecore Search: A Definitive Introduction

A key component of digital experience management is effective information retrieval. A simplified approach is required for websites, applications, and platforms containing a lot of material so that consumers can easily get the data they require. This need is met by Sitecore, a well-known name in the field of digital experience platforms (DXPs), which provides powerful and comprehensive search functionality. We will travel into the realm of Sitecore Search in this article, learning about its capabilities, architecture , and the enormous value it offers both developers and end users. Introduction to Sitecore Search    A headless content discovery platform powered by AI , Sitecore Search enables you to build predictive and custom search experiences across various content sources. To extract and ...

Sitecore NextJS Component Refactoring and Reusability


The Sitecore Headless Services provide a different ways to consume the Sitecore Content into your Head application (website) depending on your need:


The Sitecore Experience Platform is a true Headless Content Management System, and it provides the flexibility to implement a Head application with latest tech stack also, e.g., NextJS using sitecore-jss-nextjs SDK.

We can design Sitecore Renderings / Components with the help of sitecore-jss-nextjs SDK which provides the flexibility to view your web page in WYSIWYG (What You See Is What You Get) editor that allows you to easily make changes to items directly on the page. You can edit all the items that are visible on the page — text, graphics, logos, links, and so on.

Generally, the components developed using sitecore-jss-nextjs SDK are tightly coupled with the Sitecore implementation and developed as a SINGLE entity / object. It means it will not provide the reusability so that one component can be reused on multiple functionalities:
Let’s consider, you have different types of pages or content types in your Sitecore PLAY! Summit Demo Web application:
  • Session
  • Speaker
  • Vendor
  • Sponsor

All the above content types have some fields which are common, e.g.
  • Background Image
  • Heading
  • Name
And existing UI and component details are:
The component implementation details are:
In the above components details InformationPageHero component being reused in your application in different ways, e.g., to display Sponsor/Vendor/Speaker related content, and internally only one implementation handling all the different variations, so in this case instead of writing the code in one component, we can divide them into small components / renderings.

By this way we can reuse these new components in many places/times, and find below the high level of refactoring details are:
The reusability provides many benefits, and some are:
  • Easy to maintain
  • Design from reusable components is likely to cost less
  • Modularization
  • Consistency across the ecosystem
  • Loose Coupling
  • Ensure Flexibility
  • Low maintenance
In this article, we will learn how to build the Sitecore Headless Application Front-end applications renderings or components which are loosely coupled (reusable) and can be reused easily.
The refactoring process involved following steps:
Sitecore NextJS Component Refactoring and Reusability
Create Sitecore NextJS Main component

Create new rendering item Content Hero Banner (/sitecore/layout/Renderings/Project/edgewebsite/Common/ContentHeroBannerComponent) at Sitecore CMS, so that each content type (Sessions/Speakers/Vendors/Sponsors) will use the same rendering item and remove the existing rendering which render the Hero Banner Information e.g., SessionInformationPageHero, SpeakerInformationPageHero, etc..

Now create the NextJS rendering for the ContentHeroBannerComponent at /Website/src/rendering/src/components/Common/ContentHeroBannerComponent.tsx in the rendering code base:
Create different NextJS Types

NextJS Types to store the Sitecore Layout Service Data:

  • BaseContent

    Create the Sitecore NextJS type baseContent to store the common fields at /Website/src/rendering/src/types/Common/ContentBanner/baseContent.ts in the rendering code base:

    import { Field, ImageField } from '@sitecore-jss/sitecore-jss-nextjs';
    
                  export interface BaseContent {
                    Name: Field<string>;
                    Image: ImageField;
                  };
    
                  export interface SocialMediaProfiles {
                    FacebookProfileLink?: Field<string>;
                    TwitterProfileLink?: Field<string>;
                    InstagramProfileLink?: Field<string>;
                    LinkedinProfileLink?: Field<string>;
                  }
                  
  • contentBannerProps

    Create the Sitecore NextJS type contentBannerProps to store the different content types at Website\src\rendering\src\types\Common\ContentBanner\contentBannerProps.ts in the rendering code base:

    import { Room } from 'src/types/room';
                import { Day } from 'src/types/day';
                import { Timeslot } from 'src/interfaces/Timeslot';
                import { BaseContent, SocialMediaProfiles } from './baseContent';
                import { Field, ImageField } from '@sitecore-jss/sitecore-jss-nextjs';
    
    
                export type ContentBannerProps =
                  | SessionInformationPageHeroProps
                  | InformationPageHeroProps
                  | BannerProps
    
                export interface SessionInformationPageHeroProps extends BaseContent {
                  Rooms: Room[];
                  Day: Day[];
                  Timeslots: Timeslot[];
                  Premium: Field<boolean>;
                  TemplateName:string;
                }  
                export interface InformationPageHeroProps extends BaseContent,SocialMediaProfiles {
                  Rooms: Room[];
                  Day: Day[];
                  Timeslots: Timeslot[];
                  Premium: Field<boolean>;
                  Featured: Field<boolean>;
                  Picture: ImageField;
                  JobTitle: Field<string>;
                  Company: Field<string>;
                  Location: Field<string>;
                  TemplateName:string;
                  Logo: ImageField;
                  Level: Field<string>;
                }
    
    
                export type BannerProps = BaseContent
                
  • types

    Create the Sitecore NextJS type types , used to define the fields for common PageContent at Website\src\rendering\src\features\PageContent\types.ts in the rendering code base:

    import { Field } from "@sitecore-jss/sitecore-jss-nextjs"
    
                  export type DisplayNameProps =  {
                    Name: Field<string>;
                  };
    
                  export type DisplayTitleProps =  {
                    Title: string;
                  };
                  
Create Sitecore NextJS components for different content items

We have created following Sitecore NextJS components which helps to refactor the Hero Banner components:

  • InformationPageHero

    The SitecoreNextJS component InformationPageHero called from the ContentHeroBanner component to render the Hero Banner for the all the Content Type pages:

    import {  LayoutServicePageState, Text, useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
                import {
                  faFacebookF,
                  faTwitter,
                  faLinkedinIn,
                  faInstagram,
                } from '@fortawesome/free-brands-svg-icons';
                import SocialIcon from 'components/NonSitecore/SocialIcon';
                import { InformationPageHeroProps } from 'src/types/Common/ContentBanner/contentBannerProps';
                import SessionHeroBanner from './SessionHeroBanner';
                import DisplayName from 'src/features/PageContent/components/DisplayName';
                import DisplayTitle from 'src/features/PageContent/components/DisplayTitle';
    
    
                const InformationPageHero = (props: InformationPageHeroProps): JSX.Element => {
                  const { sitecoreContext } = useSitecoreContext();
    
                  const isPageEditing = sitecoreContext.pageState === LayoutServicePageState.Edit;
                  let lowerCaseQualificative ="";
                  let imageSrc="";
                 if(props.TemplateName=="Speaker")
                  {
                    lowerCaseQualificative = props?.Featured?.value ? 'featured' : '';
                    imageSrc =props.Picture.value?.src
                  }
                  else if(props.TemplateName=="Vendor" || props.TemplateName=="Sponsor")
                  {
                    lowerCaseQualificative = props?.Level?.value ? props?.Level?.value.toLocaleLowerCase() : '';
                    imageSrc =props.Logo.value?.src
                  }
    
                  const informations =
                    props.JobTitle?.value ||
                    props.Company?.value ||
                    props.Location?.value ||
                    isPageEditing ? (
                      <>
                        {props.JobTitle?.value || isPageEditing ? (
                          <div>
                            <span className="label">Job Title:</span>{' '}
                            <Text field={props.JobTitle} tag="span" />
                          </div>
                        ) : undefined}
                        {props.Company?.value || isPageEditing ? (
                          <div>
                            <span className="label">Company:</span> <Text field={props.Company} tag="span" />
                          </div>
                        ) : undefined}
                        {props.Location?.value || isPageEditing ? (
                          <div>
                            <span className="label">Location:</span>{' '}
                            <Text field={props.Location} tag="span" />
                          </div>
                        ) : undefined}
                      </>
                    ) : undefined;  
    
                    const informationsHTML = informations ? (
                      <div className="informations">{informations}</div>
                    ) : undefined;
    
                  return props.TemplateName == "Session" ? (
                    <SessionHeroBanner Premium={undefined} Rooms={[]} Day={[]} Timeslots={[]} TemplateName={props.TemplateName} {...props} />
                  ) : (
                    <section
                    className={`information-page-hero ${props.TemplateName.toLocaleLowerCase()}-information-page-hero ${lowerCaseQualificative}`}
                  >
                    <div className="content">
                      <div className="image-container">
                        {/* Purposefully not using a JSS Image component here to avoid width/height HTML attributes on the img tag */}
                        <img src={imageSrc} alt="Image" loading="lazy" />
                      </div>
                      <div className="gradient-container"></div>
                      <div className="content-container">
                        <div className={`container-content-text`}>
                          <div>
                            <DisplayTitle Title={"Meet the <span className='information-type'>" + lowerCaseQualificative + "</span> " + props.TemplateName.toLocaleLowerCase() + ":"} />            
                            <DisplayName Name={props.Name} />
                          </div>
                          {informationsHTML}
                          <div className="external-website-icons">
                            <SocialIcon Icon={faFacebookF} Link={props.FacebookProfileLink} />
                            <SocialIcon Icon={faTwitter} Link={props.TwitterProfileLink} />
                            <SocialIcon Icon={faLinkedinIn} Link={props.LinkedinProfileLink} />
                            <SocialIcon Icon={faInstagram} Link={props.InstagramProfileLink} />
                          </div>
                        </div>
                      </div>
                    </div>
                  </section>
                  )
    
                  /*
                  (
    
                    )*/
                };
    
                export default InformationPageHero;
                
  • SessionHeroBanner

    The SitecoreNextJS component SessionHeroBanner called from the InformationPageHero component if page item template name is Session, and this component will take care of rendering of Session Page Hero Banner:

    import { faCalendar, faClock, faDoorOpen } from '@fortawesome/free-solid-svg-icons';
              import InfoText from 'components/NonSitecore/InfoText';
              import { getSessionDays, getSessionTime } from 'src/helpers/DateHelper';
              import { InformationPageHeroProps } from 'src/types/Common/ContentBanner/contentBannerProps';
              import { Text } from '@sitecore-jss/sitecore-jss-nextjs';
              import DisplayName from 'src/features/PageContent/components/DisplayName';
              import DisplayTitle from 'src/features/PageContent/components/DisplayTitle';
    
              const SessionHeroBanner = (props: InformationPageHeroProps): JSX.Element => {
                const premiumSessionQualificative = props.Premium.value ? 'premium' : '';
                return (
                  <section className={`session-information-page-hero ${premiumSessionQualificative}`}>
                    <div
                      className="background-container"
                      style={{
                        backgroundImage: 'url(' + props.Image.value?.src + ')',
                      }}
                    >
                      <div className="content">
                        <div
                          className="image-container bg-cover  bg-center flex-1 min-h-full"
                          style={{
                            background:
                              'linear-gradient(to right, rgba(60, 60, 60, 0) 70%, rgba(0, 0, 0, 1) 100%)',
                          }}
                        >
                        </div>
                        <div className="content-container">
                          <div className="container-content-text">
                            <div>
                                <DisplayTitle Title={"Explore the <span className='information-type'>" + premiumSessionQualificative + "</span> session:  "} />
                                <DisplayName Name={props.Name} />
                            </div>
                            <div>
                              {/* Dispaly Multiple Rooms*/}
                              {props?.Rooms && (
                                <InfoText Icon={faDoorOpen}>
                                  {props?.Rooms.map((roomDetails, index) => (
                                      <Text style={{marginRight: 1 + 'em'}} key={index} tag="span" field={roomDetails?.fields?.Name} />
                                      ))}                    
                                </InfoText>
                              )}
                              <InfoText Icon={faCalendar}>
                                <span>{getSessionDays(props.Day)}</span>
                              </InfoText>
                              <InfoText Icon={faClock}>
                                <span>{getSessionTime(props.Timeslots)}</span>
                              </InfoText>
                            </div>             
                          </div>
                        </div>
                      </div>
                    </div>
                  </section>
                );
              };
    
              export default SessionHeroBanner;
              
Create components to display common information with common UI

We have created following Sitecore NextJS components which helps to display the common information with common UI, and reused in InformationPageHero and SessionHeroBanner components:

  • DisplayTitle

    The SitecoreNextJS component DisplayTitle called from the InformationPageHero and SessionHeroBanner component to render the Rich Text:

    import { RichText } from '@sitecore-jss/sitecore-jss-nextjs';
                import { DisplayTitleProps } from '../types';
    
                const DisplayTitle = (props: DisplayTitleProps): JSX.Element => (
    
                  <section>
                    <p className="title">
                      <RichText field={{ value: props?.Title }} />
                    </p>        
                  </section>
                );
    
                export default DisplayTitle;
                
  • DisplayName

    The SitecoreNextJS component DisplayName called from the InformationPageHero and SessionHeroBanner component to render the Text field:

    import { Text } from '@sitecore-jss/sitecore-jss-nextjs';
                import { DisplayNameProps } from '../types';
    
                const DisplayName = (props: DisplayNameProps): JSX.Element => (
                  <section>
                    <h1 className="name">
                      <Text key="{props?.fields?.Name.value}" field={props?.Name} />
                    </h1>    
                  </section>
                );
    
                export default DisplayName;
                
Update Sitecore Page Templates

You also need to remove the existing components SessionInformationPageHero/SponsorInformationPageHero/VendorInformationPageHero/SponsorInformationPageHero from page templates of content types (standard values) and need to use ContentHeroBannerComponent component, which will take care of rendering the Hero Banner for different content types.

Above refactoring details are just the one of the way to refactor the existing components in your Sitecore NextJS application code base, and generally refactoring involves lots of time and efforts, so need to be considered as important tasks in your sprint planning.
This refactored code base present at the GitHub branch


This repository is used for the primary demo code base for Sitecore Edge for Content Hub and Experience Management.

And you can check the changes directly at



Credit/References:

Pingback:
sitecore next.js sitecore jss nextjs sitecore jamstack
sitecore nextjs sample website sitecore jss dynamic placeholder sitecore-jss-nextjs github
Creating a JSS Next.js application with the JSS CLI The Sitecore Containers template for JSS Next.js apps Connecting a code-first JSS Next.js application to Sitecore
Setting up a development environment with the Sitecore Containers template for Next.js Jamstack for Sitecore JSS using Next.js with Uniform SDK Sitecore JavaScript Rendering SDK (JSS) for Next.js
Prerendering methods and data fetching strategies in JSS Next.js apps Architecture and APIs for integrating JSS Next.js apps with Sitecore editors Getting started with JSS for Next.js development
Query examples - Sitecore Documentation Start using Sitecore GraphQL API - Sitecore Documentation Sitecore Experience Edge 
next.js sitecore sitecore node js next js sitecore
next.js sample project sitecore query examples sitecore jss quick start
unit test next js sitecore nvelocity sitecore 9 exam questions and answers
nextjs best practices github next js folder structure best practices nextjs api best practices 
next js logging best practices next js styling best practices next js tips
next js routing best practices next js authentication best practices next js api best practices
next js best practices next js css best practices sitecore graphql ui 
sitecore support jobs sitecore graphql search query sitecore graphql authentication required
sitecore graphql example sitecore graphql pagination sitecore graphql api
sitecore graphql cache sitecore graphql schema sitecore graphql server cannot be reached
sitecore graphql configuration experience accelerator sitecore sitecore 8 end of life
sitecore 9 end of life sitecore graphql contextitem sitecore experience edge api  
Building custom Sitecore images sitecore component graphql query could not find type sitecore.services.graphql.dependency configurator
sitecore content hub graphql sitecore xconnect api sitecore graphql datasource
sitecore graphql extender sitecore graphql editable sitecore enable graphql
sitecore jss graphql example sitecore graphql root element is missing sitecore xconnect data analysis
sitecore graphql search examples sitecore graphql filter sitecore graphql fields
sitecore headless graphql
sitecore integrated graphql sitecore install graphql sitecore jss graphql tutorial
sitecore graphql mutation sitecore graphql Fragment Secure Sitecore JSS Site sitecore nextjs graphql
Sitecore Graphql tutorial Sitecore Performance Tuning .net core 6 graphql
sitecore 9 exam questions and answers sitecore xconnect data analytics Speed Up Sitecore Upgrade with PackageReference
sitecore graphql edge Sitecore Version 10 - Sitecore Upgrade Process sitecore graphql api
sitecore xconnect data azure sitecore xconnect data already exists contact sitecore.xconnect.xdbsearchunavailableexception
sitecore xconnect data backup sitecore analytics disable sitecore analytics
enable sitecore analytics tracker sitecore analytics testing tools sitecore analytics vs google analytics
sitecore analytics database manager how to enable sitecore analytics sitecore analytics reports
sitecore analytics not working sitecore analytics cookie sitecore analytics custom reports
sitecore analytics api sitecore analytics ip address sitecore.analytics.tracker.current.session.identify as
sitecore and google analytics adobe analytics integration with sitecore sitecore analytics benefits
best sitecore websites sitecore analytics power bi sitecore analytics chrome extension
sitecore analytics.clustername sitecore analytics.cookiedomain sitecore analytics configuration
sitecore custom analytics sitecore commerce analytics sitecore.analytics.tracker.current is null
Start using Sitecore GraphQL API Sitecore Helix Recommendation and Conventions - Helix 2.0 What’s new in Sitecore 10
Analysis for Sitecore Experience Accelerator (SXA) based website implementation Secure Sitecore JSS Site Sitecore Experience Accelerator (SXA)
Sitecore Graphql tutorial Sitecore Performance Tuning Sitecore GraphQl Examples
What is SXA Page Design? Sitecore Installation and Upgrade Guides Sitecore Upgrade Services

Comments

Popular posts from this blog

Sitecore GraphQL Queries

Sitecore Experience Manager Cloud (XM Cloud) Building blocks

Configuring Sitecore Next.js Headless SXA Multisite App in a Sitecore Container