Sitecore Articles / Blogs / Perficient https://blogs.perficient.com/tag/sitecore/ Expert Digital Insights Mon, 30 Jun 2025 19:11:20 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png Sitecore Articles / Blogs / Perficient https://blogs.perficient.com/tag/sitecore/ 32 32 30508587 From Next.js to React (and Back): Navigating Sitecore JSS SDKs the Right Way https://blogs.perficient.com/2025/06/30/from-next-js-to-react-and-back-navigating-sitecore-jss-sdks-the-right-way/ https://blogs.perficient.com/2025/06/30/from-next-js-to-react-and-back-navigating-sitecore-jss-sdks-the-right-way/#comments Mon, 30 Jun 2025 18:29:33 +0000 https://blogs.perficient.com/?p=383723

When I first started working on Sitecore XM Cloud and headless Sitecore projects, it was always Next.js. It felt like the default choice – and honestly, for a good reason. But over the past year, I’ve have seen and worked on projects that have their JSS apps in React – the standalone version, without Next.js.

This got me curious. Why React? What’s missing when we move away from Next.js? And most importantly – what should we choose when starting a new Sitecore project?

This post breaks down everything I uncovered while switching between the React and Next.js JSS SDKs – from core concepts to Sitecore-specific implementation differences. If you’re gearing up for a new Sitecore XM Cloud build or architecting a headless Sitecore app, this will help you make informed technical decisions.

 React vs Next.js – A Quick Overview

Before we compare the Sitecore JSS SDKs, it helps to understand the difference between React and Next.js, since these form the foundation for the SDKs.

React

React is a JavaScript library for building interactive user interfaces, primarily using a component-based approach.

It handles the view layer only and leaves routing, SSR, and other features up to you.

  • Created by Facebook
  • Ideal for Single Page Applications (SPAs)
  • Works entirely on the client side by default
  • Needs manual setup for routing, SSR, etc.

In short, React is flexible, but requires more manual effort.

Next.js

Next.js is a React framework that extends React by adding routing, server-side rendering, static generation, and much more – right out of the box.

  • Created by Vercel
  • Supports both SSR and SSG
  • Offers file-based routing and API routes
  • Optimized for SEO and performance
  • Often used for JAMstack or hybrid sites

In short, Next.js is React with superpowers – especially useful for performance and SEO.

React vs Next.js – Core Differences

FeatureReactNext.js
TypeLibraryFramework
RoutingManual (react-router-dom)Built-in file-based
RenderingCSR onlyCSR, SSR, SSG, ISR
SEOManual setupBuilt-in support
Image OptimizationManualBuilt-in (next/image)
API RoutesNot availableAvailable (/pages/api/)
Developer ControlHighBalanced

Sitecore JSS SDKs – What Are Your Options?

Sitecore’s JSS (JavaScript Services) offering is what enables developers to build fully headless front-end applications that communicate with Sitecore via APIs, mainly the Layout Service. JSS supports multiple front-end frameworks  – but they’re not all on equal footing when it comes to features, support, or long-term viability.

Let’s walk through each major option available, with a special focus on their corresponding packages and real-world suitability.

Supported frameworks:

  • React
  • Next.js
  • Angular (legacy support)
  • Vue.js (legacy support)

Next.js SDK for Sitecore

Package: @sitecore-jss/sitecore-jss-nextjs

This is Sitecore’s officially recommended SDK for building headless applications – especially when you’re working with Sitecore XM Cloud or public-facing websites that need server-side rendering, SEO, or personalization.

It’s built specifically to work with the Next.js framework and tightly integrates with its rendering strategies like SSR, SSG, and ISR.

Key Features:

  • Built-in support for Server-Side Rendering (SSR) and Static Site Generation (SSG)
  • Native integration with Experience Editor and Preview Mode
  • Works seamlessly with personalization, multilingual/multisite setups, and Experience Edge
  • Uses file-based routing, so Sitecore routes map easily to Next.js pages
  • Excellent performance with ISR (Incremental Static Regeneration) to update static content on demand
  • Supports middleware, API routes, and hybrid rendering patterns out-of-the-box

Ideal for: Content-heavy websites, SEO-focused applications, XM Cloud implementations, and teams that need a balance between performance and editorial flexibility.

React SDK for Sitecore

Package: @sitecore-jss/sitecore-jss-react

This SDK enables you to build headless Sitecore applications using React – without the Next.js framework. It’s typically used with Create React App (CRA), Vite, or any custom React setup.

Unlike the Next.js SDK, this one is designed for client-side rendering (CSR) only. That means you won’t get built-in SSR or SSG unless you integrate additional tools or custom server-side rendering mechanisms.

Key Features:

  • Pure React support with no dependency on a specific build tool
  • Manual routing setup using react-router-dom or other libraries
  • Layout data must be fetched manually using hooks like useEffect
  • You handle loading states, errors, and transitions manually
  • Limited or workaround-based support for Experience Editor and preview functionality
  • Still works well with Sitecore’s Layout Service, component factory, and placeholders

Ideal for: SPAs, internal tools, admin dashboards, or lightweight projects where SEO and editing support aren’t top priorities.

 While you can get the job done with this SDK, it requires significantly more boilerplate and configuration compared to Next.js –  especially for features like personalization, preview, or SSR.

Angular SDK for Sitecore

Package:  @sitecore-jss/sitecore-jss-angular

The Angular SDK allows you to build Sitecore headless applications using Angular. While it still technically works, it’s no longer getting as much attention from Sitecore. Most of the ecosystem and tutorials are focused on React and Next.js.

Key Features:

  • Angular support with Sitecore’s Layout Service integration
  • Requires more setup to enable routing and layout rendering
  • Experience Editor support is possible but less stable than React/Next.js
  • Documentation and examples are limited or outdated

Ideal for: Teams heavily invested in Angular with a legacy project that needs to interface with Sitecore in a headless manner. Not recommended for new XM Cloud projects due to limited support and community activity.

Vue SDK for Sitecore

Package: @sitecore-jss/sitecore-jss-vue

Like the Angular SDK, the Vue SDK was created to allow Vue.js developers to build JSS-based front-end apps for Sitecore. However, this SDK is largely legacy and isn’t actively maintained for new capabilities or integrations like personalization or Experience Edge.

Key Features:

  • Vue-based rendering of Sitecore Layout Service JSON
  • Basic placeholder and component support
  • Routing and SSR require additional setup
  • Experience Editor integration is limited and clunky

Ideal for: Experimental or legacy Vue-based front ends. Avoid using for XM Cloud or future-facing Sitecore work.

React Native SDK for Sitecore

Package:  @sitecore-jss/sitecore-jss-react-native

This experimental SDK is meant to enable mobile applications built in React Native to consume Sitecore layout and content via the Layout Service. While an exciting idea, this SDK is not production-ready and lacks many features you’d expect in a full headless implementation.

Key Features:

  • Allows mobile apps to render Sitecore content using components mapped from layout JSON
  • No official Experience Editor or personalization support
  • Minimal documentation and sample apps
  • Not tested against XM Cloud

Ideal for: Prototyping or experimental mobile projects. Not suitable for stable production use.

Next.js vs React SDK: A Comparison

Here’s how the two officially supported Sitecore JSS SDKs stack

FeatureReact JSS SDKNext.js JSS SDK
Rendering SupportCSR onlyCSR, SSR, SSG, ISR
RoutingManual via react-router-domFile-based routing
SEONeeds manual setupBuilt-in
Layout FetchingClient-side with useEffectIn getStaticProps / getServerSideProps
Experience EditorLimitedFully supported
Preview ModeCustom setupOut-of-the-box
PersonalizationPartialFull support
PerformanceLightweight SPAOptimized with ISR/SSG
HostingStatic (Netlify, Vercel, Azure Blob)SSR-capable platforms (Vercel, Azure App Service)

 Final Thoughts

When I first started working with Sitecore headless projects, I’ll admit – I thought Next.js was the only way. It had everything: built-in routing, SSR, Experience Editor support — and it just worked with XM Cloud.

But over time, as I got to work with the React JSS SDK, I realized it brings its own set of strengths. It gives you more control, fewer constraints, and is actually a great fit for lightweight apps, internal tools, or scenarios where SSR isn’t a must-have.

That said, if you’re building a full-blown customer-facing site with personalization, multilingual support, and editorial workflows – Next.js is still the best fit.

So no, it’s not about which one is better. It’s about picking what’s right for your project goals, team setup, and future roadmap. Both SDKs have their place – and knowing when to use what is where the real engineering choice lies.

]]>
https://blogs.perficient.com/2025/06/30/from-next-js-to-react-and-back-navigating-sitecore-jss-sdks-the-right-way/feed/ 1 383723
How to Optimize Sitecore Headless and Next.js on Vercel https://blogs.perficient.com/2025/05/22/how-to-optimize-sitecore-headless-and-next-js-on-vercel/ https://blogs.perficient.com/2025/05/22/how-to-optimize-sitecore-headless-and-next-js-on-vercel/#respond Thu, 22 May 2025 16:47:13 +0000 https://blogs.perficient.com/?p=381796

Maybe you’ve already made the switch to XM Cloud, or maybe you’re still evaluating it as the answer to all your digital delivery challenges. Spoiler alert: it won’t magically solve everything — but with the right setup and smart optimizations, it can absolutely deliver fast, scalable, and maintainable experiences.

If you’re using Sitecore Headless with Next.js, you’re already building on a modern and flexible foundation. Add in a deployment platform like Vercel, and you’ve got serious power at your fingertips. But unlocking that potential requires knowing where to fine-tune — both at the application and platform level.

Streamline Your Layout and API Payloads

The Sitecore Layout Service is versatile but can return bulky JSON payloads if left unchecked. Clean up your responses by:

  • Removing unused placeholders and renderings

  • Filtering out internal tracking or analytics fields unless explicitly needed

  • Configuring the Layout Service to tailor the response to your frontend needs

If you’re using Sitecore Search or XM Cloud with GraphQL, concise queries will help keep your pages fast and predictable

  • Request only the fields you need

  • Use first: or limit: to control result size
  • Organize queries into reusable fragments for maintainability and performance

Smaller payloads result in faster hydration, quicker time-to-interactive, and lower bandwidth usage — all especially valuable for mobile-heavy audiences.

Use Webhooks for Smarter Publishing (On-demand Revalidation or ODR)

Don’t rely on manual rebuilds or blanket cache clears. XM Cloud supports webhooks on publish, which opens the door to smarter automation:

  • Trigger on-demand ISR revalidation for updated pages

  • Push new content to Edge Config, CDNs, or search indexes

  • Notify external systems (e.g., analytics, commerce, personalization) immediately

It’s the best way to keep content fresh without sacrificing performance or rebuilding the entire site.

Choose the Right Rendering Method: SSR, SSG, or ISR?

Not every page needs to be dynamic, and not every page should be static. Picking the right rendering strategy is critical — especially in a Sitecore headless app where you’re mixing marketing content with personalization and real-time updates.

Here’s how to decide:

Use SSR (Server-Side Rendering) when:

  • The page depends on the user session or request (e.g., personalization, authenticated pages)

  • You’re rendering in preview mode for content authors

Use SSG (Static Site Generation) when:

  • The content rarely changes (e.g., static landing pages or campaigns)

  • You want instant load times and no server cost

Use ISR (Incremental Static Regeneration) when:

  • Content changes periodically, but not per-request

  • You want to combine the speed of static with the freshness of dynamic

Use next/link with Prefetching

If you’re still using regular <a> tags or not thinking about navigation performance, this one’s for you. The next/link component enables fast, client-side routing and automatic prefetching of pages in the background.

Example:

import Link from 'next/link';

<Link href="/products" prefetch={true}>About Us</Link>
  • Use it for all internal links

  • Set prefetch={true} on high-priority routes

  • Check behavior in your browser’s network tab — look for .json page data being fetched in advance

This alone can make your site feel instantly faster to users.

Optimize Fonts with next/font

Sitecore headless apps don’t include next/font by default, but it’s worth integrating. It allows you to self-host fonts in a performance-optimized way and avoid layout shifts.

Example:

import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

Apply fonts globally or per-page to improve loading consistency and avoid FOUT (Flash of Unstyled Text). Better fonts = better user experience.

Clean Up Your Codebase

Performance isn’t just about server-side logic — it’s also about keeping your codebase lean and clean.

What to review:

  • Old personalization plugins that are no longer used

  • Middleware that’s too permissive or generic in its matching

  • Outdated multisite logic if you’ve already split into multiple Vercel projects

  • Unused components or fetch logic in shared utilities

Use Vercel performance insights to track slow routes and spot cold starts.

Enable Fluid Compute

Fluid Compute lets Vercel reuse idle time across your serverless functions. That means better performance and lower costs — without any code changes.

To enable it:

  • Go to your Vercel project settings

  • Navigate to Functions

  • Toggle Fluid Compute on

You can monitor the impact under Observability → Logs in your dashboard. It’s a low-effort win. Read more details about Fluid Compute in my previous blog!

Be Selective with Middleware

Next.js middleware is powerful but potentially expensive in performance terms. Use it wisely:

  • Limit middleware to only essential routes

  • Avoid using fetch() inside middleware — use Edge Config instead

  • Replace multisite plugins with separate Vercel projects

  • Audit unused or legacy logic, especially leftover personalization

Track middleware behavior through the Middleware tab in Vercel Logs.

Manage Redirects with Edge Config

For the fastest possible redirects, manage them directly in Vercel using Edge Config. This keeps Sitecore out of the request path and ensures instant resolution at the edge.

  • Store all redirect data in Edge Config
  • Deploy updates as part of your app or via external config tools
  • Avoid real-time fetches from Sitecore for redirect logic

If you’re managing a large volume of redirects, consider using a bloom filter to optimize memory usage. Just note that bloom filters introduce a small delay due to redirect verification.

Conclusion

Optimizing a Sitecore Headless application, especially one deployed on Vercel, is about making dozens of small, smart decisions that add up to big wins in performance, scalability, and developer happiness. Whether it’s pruning your Layout Service output or toggling a setting in your Vercel dashboard, each move brings you closer to a faster, more responsive site.

XM Cloud doesn’t come pre-optimized — but that’s actually a good thing. It gives you the power and flexibility to build the way you want. Just make sure you’re building it right.

Optimization Checklist

Sitecore & XM Cloud

  • Prune Layout Service JSON (remove unused placeholders and fields)

  • Use GraphQL efficiently (limit queries, use fragments)

  • Set up publish webhooks for on-demand rendering or cache purging

Rendering Strategy

  • Use SSR for personalized/authenticated content

  • Use SSG for static pages

  • Use ISR for hybrid performance/freshness

Next.js

  • Replace <a> with next/link and enable prefetching

  • Add next/font for consistent and fast font rendering

Vercel

  • Enable Fluid Compute for better serverless efficiency

  • Use middleware only where necessary and avoid fetch inside

  • Use Edge Config for fast redirect handling

  • Monitor logs and performance insights for slow routes and cold starts

]]>
https://blogs.perficient.com/2025/05/22/how-to-optimize-sitecore-headless-and-next-js-on-vercel/feed/ 0 381796
Sitecore Personalize: Advanced Page Targeting https://blogs.perficient.com/2025/05/21/sitecore-personalize-advanced-page-targeting/ https://blogs.perficient.com/2025/05/21/sitecore-personalize-advanced-page-targeting/#respond Wed, 21 May 2025 15:57:14 +0000 https://blogs.perficient.com/?p=381706

In my previous blog https://blogs.perficient.com/2024/07/01/sitecore-personalize-close-event-logic/, I shared a method for using cookies to enable the user to dismiss an alert banner.  This process involved writing the cookie when the user clicks the close icon and checking for the cookie when the experience is displayed.  This approach worked well because the entirety of the javascript code could be stored in the web template.  This ensures the code to check for the cookie can’t be missed or forgotten when the template was used.  Unfortunately, this had an unintended side effect.  Personalize still executed the experience and counted it in the analytics and performance metrics even though the javascript never added the elements to the screen.  This lead to overinflated metrics and the inability to use the data for accurate forecasting.  This problem can be overcome with advanced page targeting.

Advanced Page Targeting

Advanced page targeting allows you to run client side javascript to decide if an experience should execute.  Since the javascript runs client side, you can read the url and query string parameters, you can access the console to log messages, you can access the document object to query selectors on the page, and of course read cookies.  Advanced page targeting javascript runs after the page loads, but before the experience loads which allows us to prevent the experience from executing and overinflating analytics.  Be sure to keep your script as lean and performant as possible to reduce the possible screen flicker of loading the experience after the page has loaded.

Checking Cookies with Advanced Page Targeting

In order to use advanced page targeting, you must select the “specific pages” setting under “Page Targeting”.

Pers Apt Enable

Enable page targeting on specific pages to enable advanced page targeting

In the flyout window for page targeting, add any filters for pages where you want the experience to display.  If you want the experience to display on all pages, simply use the contains “/” rule.  In the advanced targeting section, click the “add script” button.

Pers Apt Configure

Configure advanced page targeting

In the advanced targeting code editor, enter the following code.

(function() {
   // console.log(new Error().stack);

    var cookies = document.cookie.split(';');
    for (var i = 0; i < cookies.length; i++) {
        var cookie = cookies[i].trim();
        if (cookie.indexOf('pers-' + selectedVariant.ref + '=clicked') === 0) {
            return false;
        }
    }

    targetingPassed();
})();

 

In my previous blog, I created the cookie using the variant.ref as part of the cookie name.  This adds the guid of the current experience id to the name of the cookie making it unique even when the web template was reused.  Advanced targeting does not have access to the variant.ref property.  So it doesn’t work with the close event logic idea I came up with before.  However, there is a selectedVariant.ref property that you can use.  Using selectedVariant.ref works for both experiences and experiments as it can dynamically get the correct guid of the current variant at run time.

When personalize evaluates this block of javascript code, it only continues processing the flow of execution for the experience if the “targetingPassed()” function is called.  If you want to stop the flow of execution, return false.  In our case, we want to stop the flow of execution if the cookie is found.  Otherwise we can call the targetingPassed() function and allow the experience to display to the user.

Once you have added advanced targeting javascript, you can edit the script by clicking the pencil icon.

Pers Apt Edit

Edit advanced page targeting settings

 

Pers Apt Editscript

Edit advanced page targeting script

Things to Know About Advanced Page Targeting

As mentioned, this solution solves the problem of overinflating analytics.  The downside is the advanced page targeting settings and code cannot be saved with the web template.  So the marketer or content author must remember to enable this setting and add the javascript code to the experience.  If the code is not added, the user will be able to close the experience, but it will display on the next page load since the code to check for the cookie is missing.  One other thing to note, if you change your page targeting settings back to “all pages”, the advanced targeting script is deleted from the experience.  In both cases, ensure that it is easy for content authors to find and reuse.

]]>
https://blogs.perficient.com/2025/05/21/sitecore-personalize-advanced-page-targeting/feed/ 0 381706
Getting Started with Personalization in Sitecore XM Cloud: Enable, Extend, and Execute https://blogs.perficient.com/2025/05/20/getting-started-with-personalization-in-sitecore-xm-cloud-enable-extend-and-execute/ https://blogs.perficient.com/2025/05/20/getting-started-with-personalization-in-sitecore-xm-cloud-enable-extend-and-execute/#respond Tue, 20 May 2025 07:47:48 +0000 https://blogs.perficient.com/?p=381481

 

In my previous blog – Personalization in Sitecore XM Cloud: What’s New, What’s Different, and What It’s Built On!, I explored what makes personalization in Sitecore XM Cloud so powerful – from its cloud-native infrastructure and headless architecture to the built-in rule-based targeting engine. Now, I’m shifting from concepts to implementation. In this post, I’ll walk you through how to enable personalization using analytics identifiers that track and categorize visitor behavior in real time. Then, I’ll show how to use Page Builder to create audience-specific page variants, and personalize components – all with a few clicks, not code.

In Sitecore XM Cloud, personalization happens primarily at the page level. The process revolves around creating page variants and personalizing components. This functionality is fully integrated into Page Builder, allowing you to define variations of the page based on different personalization criteria.

Key Points:

  • Personalization is configured directly within Page Builder on a per-page basis.
  • You can create page variants – each tailored to specific audiences or conditions.
    • Individual components on the page can be personalized : swapped, hidden and can be edited to show different content
  • All of this is managed via the Personalize tab available on the page within Page Builder.

How to Check if Personalization is Enabled:

To determine whether personalization is set up:

  • Go to the Page Builder for the desired page.
  • Click on the Personalize tab.

Personalization Notenabled

 

If personalization is not enabled, you will see :

  • No variant options available.
  • A prompt or empty state indicating no personalization rules are define.

 

Personalization Enabled

If personalization is enabled, you’ll see:

  • Variants listed based on configured rules.
  • Options to add/edit rules and personalize individual components.

 

 

Adding Analytics Identifiers to Enable Personalization

Analytics identifiers are essential for enabling personalization in Sitecore XM Cloud. They help track visitors, manage sessions, and unify data across sites for a complete view of user behavior. Without them, personalization rules and targeted content delivery won’t function. Assigning the same identifier to multiple sites allows for consolidated analytics, providing a holistic view of user engagement across different sites.

Step 1: Access Site Settings

  1. Navigate to the XM Cloud Portal: Log in to your XM Cloud environment.
  2. Open the Sites Dashboard: In the top navigation bar, click on Sites to view all your sites.
  3. Select the Desired Site: Locate the site you wish to configure. Click on the Actions menu (three dots) associated with that site and select Settings.
  4. Select the Site hosts: Browse through the options on the left side and select Site hosts option and then select the desired host

Personalization Site Hosts

Step 2: Add or Assign an Analytics Identifier

  1. Navigate to the Analytics and Personalization Tab: Within the site settings, click on the Analytics and Personalization tab.
  2. Add Identifier for a Language: In the list of languages associated with the site, click the plus (+) icon next to the language you want to configure

Personalization Analytics And Personalization Tab

Step 3: Choose Identifier Type:

    • Create a New Identifier: Select the Create new tab.
      • Name the Identifier: Provide a unique name for the analytics identifier.
      • Set Session Timeout: Define the session timeout duration (default is 30 minutes).
    • Assign an Existing Identifier: Select the Assign existing tab.
      • Select Identifier: Choose from the list of existing identifiers available in your environment.

Personalization Add Analytics Identifier

Let’s Personalize a Page in Sitecore XM Cloud

  1. Navigate to the Page in the Page Builder
  • From the XM Cloud portal, open your site and select the page you want to personalize.
  • Once it loads in Page Builder, you’ll see a “Personalize” tab at the top navigation bar if personalization is enabled.

Personalize Tab

  1. Create a Page Variant
  • Click on the Personalize tab.
  • Select Create Variant.
  • Provide a name for the variant (e.g. “Every Monday Campaign”)

Create Variant

  • Click Next to proceed to audience configuration.

Create Audience

  1. Define the Audience for the Variant

Available Condition Categories

 

Click Add first condition to define who should see this variant.

Choose from the available conditions such of different tags

 

 

  • Configure the selected condition using the provided fields.

Condition

  • You can add multiple conditions using AND/OR logic.

Multiple Conditions

 

 

Personalization Options Available

 

  1. Customize the Variant
  • Once you click on Save, your variant is created and the audience is defined, you can now edit the variant.
  • On the side is a screenshot of the Page Builder that lists down all that we can do with personalization on the page.

 

 

 

 

Personalized Component

  • You’ll see a color-coded border or indicator showing that you’re editing a personalized version.
  • Each change you make is isolated to that variant and won’t affect the default view.

 

  1. Preview the Variant
  • Use the Preview feature to see what the page will look like for the targeted audience.
  1. Publish the Variant
  • When satisfied, click Publish to activate your personalized variant.
  • Sitecore XM Cloud will now automatically show the appropriate variant to users matching the defined audience.

 

Personalization in Sitecore XM Cloud isn’t just a feature – it’s a strategic advantage. By enabling analytics identifiers and using Page Builder to create tailored experiences, you can deliver the right content to the right audience at the right time. With this setup in place, you’re well on your way to building smarter, more engaging digital experiences.

In the next blog, I’ll take you behind the scenes of how personalized content is actually delivered in Sitecore XM Cloud. We’ll explore the request lifecycle, how the Cloud SDK plays a role in fetching and rendering personalized variants, and the technical flow that powers real-time targeting. This deeper dive will help you understand not just what personalization looks like, but how it works under the hood.

 

]]>
https://blogs.perficient.com/2025/05/20/getting-started-with-personalization-in-sitecore-xm-cloud-enable-extend-and-execute/feed/ 0 381481
What’s the point of Headless? https://blogs.perficient.com/2025/04/23/whats-the-point-of-headless/ https://blogs.perficient.com/2025/04/23/whats-the-point-of-headless/#respond Wed, 23 Apr 2025 17:49:11 +0000 https://blogs.perficient.com/?p=379825

In the ever-evolving world of web development, the term “headless” is as popular today as it ever has been. But what does it really mean, and why should you care? Let’s dive into the concept of headless architecture, its benefits, and why Sitecore is leading the charge in this space.

What is Headless?

At its core, a headless CMS is a software design approach that separates the front-end (what users see) from the back-end (where content is managed). Unlike traditional CMS platforms that tightly couple content management with presentation, headless CMS’s use APIs to deliver content anywhere—web, mobile app, kiosk, or a smart device. In many ways, the originator of headless architecture is Jamstack – which stands for JavaScript, APIs, and Markup. Instead of relying on traditional monolithic architectures, Jamstack applications decouple the web experience from the back-end, making them more scalable, flexible, and high-performing. JavaScript handles dynamic interactions on the front-end, allowing developers to build fast and modern user experiences. API’s provide a way to pull in content and services from various sources and the website can also push data to API’s such as form submissions, custom analytics events and other user driven data. Markup refers to pre-built HTML that can be served efficiently, often generated using static site generators or frameworks like Next.js.

Why Go Headless?

You might be wondering, “Why would I build my website entirely in JavaScript when it’s mostly content?” That’s a valid question and I thought the same when Sitecore JSS first came out. Headless though is less about “building your site in JavaScript” and more about the benefits of the architecture.

Flexibility

Headless CMS’s allow developers to work with any front-end framework they choose, whether it’s React, Vue, Angular, or whatever your favorite framework might be. This means teams are not locked into the templating system of a traditional CMS or the underlying backend technology.

Performance

Speed is everything in today’s digital landscape. Studies show that even a slight delay in page load time can significantly impact user engagement and conversion rates. Headless CMS’s improve performance by enabling static site generation (SSG) and incremental static regeneration (ISR)—both of which ensure lightning-fast load times. Instead of a server processing each request from a user, static content can be served from a global CDN – which is a modern composable architecture. Of course, server side rendering is also still an option and can also by very performant with the right caching strategy.

Omnichannel Delivery

Content today is consumed on more than just websites. Whether it’s a mobile app, smart device, digital kiosk, or even a wearable, headless architecture ensures content can be delivered anywhere through API’s. This makes it easier for brands to maintain a consistent digital experience across multiple platforms without duplicating content.

Security

Traditional CMS’s are often vulnerable to security threats because they expose both the content management system and the front-end to potential attacks. In contrast, headless CMS’s separate these layers, reducing the attack surface. With content served via APIs and front-end files hosted on secure CDNs, businesses benefit from enhanced security and fewer maintenance headaches.

Scalability

Handling high traffic volumes is a challenge for traditional CMS platforms, especially during peak times. Since headless solutions rely on cloud-based infrastructure, they can scale dynamically without requiring expensive hardware upgrades. Whether you’re serving thousands or millions of users, headless architecture ensures your site remains stable and responsive.

Why Sitecore for Headless?

There are plenty of options in the headless CMS market, but Sitecore offers a unique blend of features that make it stand out. With XM Cloud, Sitecore provides a fully SaaS-based solution—no more infrastructure headaches, no more costly upgrades, and uptime and reliability are now handled by Sitecore.

Sitecore’s hybrid headless approach allows organizations to transition at their own pace, leveraging the benefits of headless while maintaining familiar content management workflows. Hybrid headless gives content authors complete freedom and flexibility to build content however they’d like – where most purely headless content management systems are more rigid on how pages are built.

As digital experiences become more dynamic and user expectations continue to rise, headless CMS solutions offer the agility businesses need. If you’re looking to modernize your digital strategy, now is the time to embrace headless.

]]>
https://blogs.perficient.com/2025/04/23/whats-the-point-of-headless/feed/ 0 379825
How the Change to TLS Certificate Lifetimes Will Affect Sitecore Projects (and How to Prepare) https://blogs.perficient.com/2025/04/18/how-the-change-to-tls-certificate-lifetimes-will-affect-sitecore-projects-and-how-to-prepare/ https://blogs.perficient.com/2025/04/18/how-the-change-to-tls-certificate-lifetimes-will-affect-sitecore-projects-and-how-to-prepare/#respond Fri, 18 Apr 2025 13:54:17 +0000 https://blogs.perficient.com/?p=380286

TLS certificate lifetimes are being significantly reduced over the next few years as part of an industry-wide push toward greater security and automation. Here’s the phased timeline currently in place:

  • Now through March 15, 2026: Maximum lifetime is 398 days

  • Starting March 15, 2026: Reduced to 200 days

  • Starting March 15, 2027: Further reduced to 100 days

  • Starting March 15, 2029: Reduced again to just 47 days

For teams managing Sitecore implementations, this is more than a policy shift—it introduces operational urgency. As certificates begin expiring more frequently, any reliance on manual tracking or last-minute renewals could result in costly downtime or broken integrations.

If your Sitecore environment includes secure endpoints, custom domains, or external integrations, now is the time to assess your certificate strategy and move toward automation.

Why This Matters for Sitecore

Sitecore projects often involve:

  • Multiple environments (development, staging, production) with different certificates

  • Custom domains or subdomains used for CDNs, APIs, headless apps, or marketing campaigns

  • Third-party integrations that require secure connections

  • Marketing and personalization features that rely on seamless uptime

A single expired certificate can lead to downtime, loss of customer trust, or failed integrations—any of which could severely impact your digital experience delivery.

Key Risks of Shorter TLS Lifetimes

  • Increased risk of missed renewals if teams rely on manual tracking

  • Broken environments due to expired certs in Azure, IIS, or Kubernetes configurations

  • Delayed deployments when certificates must be re-issued last minute

  • SEO and trust damage if browsers start flagging your site as insecure

How to Prepare Your Sitecore Project Teams

To stay ahead of the TLS certificate lifecycle changes, here are concrete steps you should take:

1. Inventory All TLS Certificates

  • Audit all environments and domains using certificates

  • Include internal services, custom endpoints, and non-production domains

  • Use a centralized tracking tool (e.g., Azure Key Vault, HashiCorp Vault, or a certificate management platform)

2. Automate Certificate Renewals

  • Wherever possible, switch to automated certificate issuance and renewal

  • Use services like:

    • Azure App Service Managed Certificates

    • Let’s Encrypt with automation scripts

    • ACME protocol integrations for Kubernetes

  • For Azure-hosted Sitecore instances, leverage Key Vault and App Gateway integrations

3. Establish Certificate Ownership

  • Assign clear ownership of certificate management per environment or domain

  • Document who is responsible for renewals and updates

  • Add certificate health checks to your DevOps dashboards

4. Integrate Certificate Checks into CI/CD Pipelines

  • Validate certificate validity before deployments

  • Fail builds if certificates are nearing expiration

  • Include certificate management tasks as part of environment provisioning

5. Educate Your Team

  • Hold knowledge-sharing sessions with developers, infrastructure engineers, and marketers

  • Make sure everyone understands the impact of expired certificates on the Sitecore experience

6. Test Expiry Scenarios

  • Simulate certificate expiry in non-production environments

  • Monitor behavior in Sitecore XP and XM environments, including CD and CM roles

  • Validate external systems (e.g., CDNs, integrations, identity providers) against cert failures

Final Thoughts

TLS certificate management is no longer a “set it and forget it” task. With shorter lifetimes becoming the norm, proactive planning is essential to avoid downtime and ensure secure, uninterrupted experiences for your users.

Start by auditing your current certificates and work toward automating renewals. Make certificate monitoring part of your DevOps practice, and ensure your Sitecore teams are aware of the upcoming changes.

Action Items for This Week:

  • Identify all TLS certificates in your Sitecore environments

  • Document renewal dates and responsible owners

  • Begin automating renewals for at least one domain

  • Review Azure and Sitecore documentation for certificate integration options

]]>
https://blogs.perficient.com/2025/04/18/how-the-change-to-tls-certificate-lifetimes-will-affect-sitecore-projects-and-how-to-prepare/feed/ 0 380286
Exact Match Search with Sitecore Search https://blogs.perficient.com/2025/04/17/exact-match-search-with-sitecore-search/ https://blogs.perficient.com/2025/04/17/exact-match-search-with-sitecore-search/#respond Thu, 17 Apr 2025 12:10:46 +0000 https://blogs.perficient.com/?p=380205

Searching for content on the web has evolved from basic string based matches to a sophisticated array of approaches including keywords, stemming, synonyms, word order, regular expressions, weights and relevance.  Users expect the highest ranking results to be the most relevant and 75% of users don’t go past the first page of results.  All of these advanced techniques are great to find relevant content.  But sometimes you need to find an exact phrase with the specific words in a specific order.  Many search engines do this by wrapping quote marks around the “search term” to indicate an exact match search. Sitecore Search defaults to relevance based searches, but you can achieve exact match search with some configuration.

Understanding Sitecore Search

Let’s take a moment to remember a few concepts in Sitecore Search to understand the configuration better.

  • Index Document – A single piece of indexed content such as a webpage, a word document, a pdf, etc.
  • Attributes – The fields of an indexed document such as title, subtitle, url, content type, etc.
  • Textual Relevance – Defines the attributes used to locate potential results.
  • Weight – Defines a relative value for how important an attribute is within the textual relevance.
  • Analyzers – Convert the original search query into a format that is optimized for search.
  • Token – A chunk of the original search query, usually a single word or phrase that is often modified by the analyzer to include synonyms, remove stop words and reformat to the root word.

Sitecore Search has a number of predefined analyzers built in.  Each analyzer processes the search query in different ways.

The default analyzer is the multi local standard analyzer.  This analyzer modifies the search query by making it lower case, splitting the search query into single word tokens, finding the root of each word, applying synonyms, and removing punctuation.  For this reason, it will not find an exact match.  For that we need the keyword analyzer which leaves the search query in a single token without applying any modifications.

Configure Exact Match Search – Textual Relevance

In order to configure exact match search, we need to add the keyword analyzer to the textual relevance settings for the desired attribute, in this case the description.

Navigate to Admin/Domain Settings then click the feature configuration tab.

Sc Textual Reference A

Domain Settings

Edit the Textual Relevance section.

Sc Textual Referenceb

Textual Relevance Settings

Add the keyword analyzer to the description attribute.

Sc Textual Reference C

Add Analyzer

Sc Textual Reference D

Select the keyword analyzer

Make sure to save your changes then publish your domain settings for your changes to take effect.

Configure Exact Match Search – Widget Settings

Next we need to configure our search widget to use our textual relevance settings.

Navigate to a widget variation and click add rule.

Sc Textual Reference 1

Add rule to a widget Variation

 

Click the top icon on the left to set the site context.  Add a context rule for Keyword and select the contains option.  In the input box, type a single quote mark.

Sc Textual Reference 2

Add keyword rule to the site context

Click the bottom icon on the left to configure the settings.  Click the tab for Textual Relevance and click the toggle to enable the configuration.  Notice that the description field is listed twice, once for each analyzer. From here you can enable/disable each attribute/analyzer and set its relative weight.  In this example, I’ve set the description-keyword to 3 and the name-multilocal to 1.  This will do the exact match search only on the description attribute.  You could include name-keyword analyzer to do an exact match on the name as well if that is desired.

Sc Textual Reference 3

Description keyword rule

Repeat the process to add or modify a second rule that uses the description-multilocal analyzer.

Sc Textual Reference 4 Rule2

Description multilocale rule

This rule will be the fallback if the search term does not include a quote.

Sc Textual Reference 5

Rule order and fallback

Exact Match Search in Action

With this configuration in place, you can see the difference in the search results.  In this example, I’ve searched for “proxy statements”.

When you include a quote mark in the search term, you only get results that have the exact phrase “proxy statements”.  This search returns 12 results.

Sc Textual Reference B1

Exact match search with 12 results

When you do not include the quote mark in the search term, you get results that include proxy, statements and statement.  This search returns 68 results.

Sc Textual Reference A0

Relevance search with 68 results

]]>
https://blogs.perficient.com/2025/04/17/exact-match-search-with-sitecore-search/feed/ 0 380205
Security Best Practices in Sitecore XM Cloud https://blogs.perficient.com/2025/04/16/security-best-practices-in-sitecore-xm-cloud/ https://blogs.perficient.com/2025/04/16/security-best-practices-in-sitecore-xm-cloud/#respond Wed, 16 Apr 2025 23:45:38 +0000 https://blogs.perficient.com/?p=380233

Securing your Sitecore XM Cloud environment is critical to protecting your content, your users, and your brand. This post walks through key areas of XM Cloud security, including user management, authentication, secure coding, and best practices you can implement today to reduce your security risks.

We’ll also take a step back to look at the Sitecore Cloud Portal—the central control panel for managing user access across your Sitecore organization. Understanding both the Cloud Portal and XM Cloud’s internal security tools is essential for building a strong foundation of security.


Sitecore Cloud Portal User Management: Centralized Access Control

The Sitecore Cloud Portal is the gateway to managing user access across all Sitecore DXP tools, including XM Cloud. Proper setup here ensures that only the right people can view or change your environments and content.

Organization Roles

Each user you invite to your Sitecore organization is assigned an Organization Role, which defines their overall access level:

  • Organization Owner – Full control over the organization, including user and app management.

  • Organization Admin – Can manage users and assign app access, but cannot assign/remove Owners.

  • Organization User – Limited access; can only use specific apps they’ve been assigned to.

Tip: Assign the “Owner” role sparingly—only to those who absolutely need full administrative control.

App Roles

Beyond organization roles, users are granted App Roles for specific products like XM Cloud. These roles determine what actions they can take inside each product:

  • Admin – Full access to all features of the application.

  • User – More limited, often focused on content authoring or reviewing.

Managing Access

From the Admin section of the Cloud Portal, Organization Owners or Admins can:

  • Invite new team members and assign roles.

  • Grant access to apps like XM Cloud and assign appropriate app-level roles.

  • Review and update roles as team responsibilities shift.

  • Remove access when team members leave or change roles.

Security Tips:

  • Review user access regularly.

  • Use the least privilege principle—only grant what’s necessary.

  • Enable Multi-Factor Authentication (MFA) and integrate Single Sign-On (SSO) for extra protection.


XM Cloud User Management and Access Rights

Within XM Cloud itself, there’s another layer of user and role management that governs access to content and features.

Key Concepts

  • Users: Individual accounts representing people who work in the XM Cloud instance.

  • Roles: Collections of users with shared permissions.

  • Domains: Logical groupings of users and roles, useful for managing access in larger organizations.

Recommendation: Don’t assign permissions directly to users—assign them to roles instead for easier management.

Access Rights

Permissions can be set at the item level for things like reading, writing, deleting, or publishing. Access rights include:

  • Read

  • Write

  • Create

  • Delete

  • Administer

Each right can be set to:

  • Allow

  • Deny

  • Inherit

Best Practices

  • Follow the Role-Based Access Control (RBAC) model.

  • Create custom roles to reflect your team’s structure and responsibilities.

  • Audit roles and access regularly to prevent privilege creep.

  • Avoid modifying default system users—create new accounts instead.


Authentication and Client Credentials

XM Cloud supports robust authentication mechanisms to control access between services, deployments, and repositories.

Managing Client Credentials

When integrating external services or deploying via CI/CD, you’ll often need to authenticate through client credentials.

  • Use the Sitecore Cloud Portal to create and manage client credentials.

  • Grant only the necessary scopes (permissions) to each credential.

  • Rotate credentials periodically and revoke unused ones.

  • Use secure secrets management tools to store client IDs and secrets outside of source code.

For Git and deployment pipelines, connect XM Cloud environments to your repository using secure tokens and limit access to specific environments or branches when possible.


Secure Coding and Data Handling

Security isn’t just about who has access—it’s also about how your code and data behave in production.

Secure Coding Practices

  • Sanitize all inputs to prevent injection attacks.

  • Avoid exposing sensitive information in logs or error messages.

  • Use HTTPS for all external communications.

  • Validate data both on the client and server sides.

  • Keep dependencies up to date and monitor for vulnerabilities.

Data Privacy and Visitor Personalization

When using visitor data for personalization, be transparent and follow data privacy best practices:

  • Explicitly define what data is collected and how it’s used.

  • Give visitors control over their data preferences.

  • Avoid storing personally identifiable information (PII) unless absolutely necessary.


Where to Go from Here

Securing your XM Cloud environment is an ongoing process that involves team coordination, regular reviews, and constant vigilance. Here’s how to get started:

  • Audit your Cloud Portal roles and remove unnecessary access.

  • Establish a role-based structure in XM Cloud and limit direct user permissions.

  • Implement secure credential management for deployments and integrations.

  • Train your developers on secure coding and privacy best practices.

The stronger your security practices, the more confidence you—and your clients—can have in your digital experience platform.

]]>
https://blogs.perficient.com/2025/04/16/security-best-practices-in-sitecore-xm-cloud/feed/ 0 380233
AI in Sitecore: Transforming Digital Experiences https://blogs.perficient.com/2025/04/16/ai-in-sitecore-transforming-digital-experiences/ https://blogs.perficient.com/2025/04/16/ai-in-sitecore-transforming-digital-experiences/#respond Wed, 16 Apr 2025 08:46:26 +0000 https://blogs.perficient.com/?p=380011

AI transforms how businesses create, maintain, and provide engaging content in Sitecore. Embedding AI, Sitecore allows developers, marketers, and IT professionals to improve workflows, enhance customer interaction, and fine-tune digital strategies. Let’s explore how AI is shaping Sitecore and what it means for businesses.

Key AI Features in Sitecore

From Content Hub to XM Cloud, products in Sitecore’s portfolio have embedded AI that provides speed and scalability to personalization. Noteworthy features include:

  • AI-powered content creation: Marketers can utilize AI to write articles, rephrase language, or write content for a certain audience. This minimizes manual effort and allows teams to focus on their strategy projects.
  • Personalization at Scale: AI leverages user behavior to provide scaled personalized engagement, like personalized product recommendations, or personalizing dynamic page layouts. This improves customer engagement and conversion rates remain on the rise.
  • Smart Search: Based on user intents and behaviors, Sitecore delivers relevant results through AI-enabled smart search capabilities for visitors, so that they can find the information they are looking for in the blink of an eye, enriching their overall experience.
  • A/B Testing: Real-time multi-armed bandit machine learning algorithms will continuously optimize the variations of content that an end user sees, showing the most efficient version of a web page or campaign.
  • Augmented Analytics: AI improves the analysis of consumer data by detecting trends and patterns that support improved decision-making.

Advantages of Integrating AI with Sitecore

There are several important benefits for organizations with embedded AI in Sitecore:

  • Improved Efficiency: Automating repetitive tasks such as content tagging, approval procedures, and translations allows focusing on more valuable activities.
  • Enhanced Creativity: New product offerings like Sitecore Stream, brand-aware assistant, give marketers agility to work with brand assets in new ways that align creativity across brand campaigns.
  • Predictive Modeling: With advanced machine learning algorithms, patterns are identified in client behaviors, which enable organizations to make data-informed predictions and improve decision-making, leading to better business outcomes.
  • Improved Security: AI can identify patterns in data that suggest unexpected behavior, which could mean a security breach or fraudulent activity, enhancing website safety.

Sector-Wise Applications

Sitecore AI deployment is also being used widely across multiple verticals:

  • Retail: Tailored product recommendations are based on browsing history, which helps boosting sales, and enhance the user experience.
  • Media and Entertainment: Automated video content tagging simplifies media management workflows.
  • Global Businesses: Businesses use AI translation programs to adapt content more quickly for a larger audience.

Challenges and Considerations

However, despite the clear benefits, integrating Sitecore with AI is not without its challenges. Organizations are forced to navigate additional challenges such as data security, implementation costs, and making sure the AI outputs maintain their brand identity. Skilled personnel are needed to manage these advanced tools effectively.

Conclusion

Sitecore is evolving into a high-performance, AI-infused platform that powers personalized digital experiences at scale. Sitecore provides businesses with the tools they need to automate tasks, encourage creativity, and derive actions from data analytics, allowing businesses to stay relevant in an ever-changing environment. In a time where upstanding customer relationships are just as important as an online approach, leveraging AI with their Sitecore development strategy can do wonders.

]]>
https://blogs.perficient.com/2025/04/16/ai-in-sitecore-transforming-digital-experiences/feed/ 0 380011
Disabling Cookies in Sitecore SDKs https://blogs.perficient.com/2025/03/12/disabling-cookies-in-sitecore-sdks/ https://blogs.perficient.com/2025/03/12/disabling-cookies-in-sitecore-sdks/#comments Wed, 12 Mar 2025 20:51:03 +0000 https://blogs.perficient.com/?p=378419

Intro 📖

In this post, we’ll take a look at a couple Sitecore SDKs commonly used to build XM Cloud head applications. Specifically, we’ll be looking at how to disable cookies used by these SDKs. This can be useful for data privacy and/or regulatory compliance reasons. These SDKs allow your application to integrate with other composable Sitecore services like analytics, personalization, and search. The cookies these SDKs use need to be considered as part of your application’s overall data protection strategy.

It’s worth noting that, even without any additional SDKs, an XM Cloud head application can issue cookies; see XM Cloud visitor cookies for more information.

Sitecore Cloud SDK ☁

The Sitecore Cloud SDK allows developers to integrate with Sitecore’s Digital Experience Platform (DXP) products. These include Sitecore CDP, Sitecore Personalize, etc. You can read the official documentation here. To learn more about the first-party cookies used by this SDK, see Cloud SDK cookies. These cookies include:

  • sc_{SitecoreEdgeContextId}
    • Stored in the browser when the SDK’s initialization function is called (more on this function later).
  • sc_{SitecoreEdgeContextId}_personalize
    • Needed to run A/B/n tests; configured in the addPersonalize() function. Also stored in the browser when the initialization function is called.

📝 Sitecore are actively working on integrating the disparate Sitecore SDKs into the Sitecore Cloud SDK. The latest version, 0.5, was released on January 29, 2025, and added search capabilities (see the XM Cloud changelog entry here). As Sitecore’s Technical Product Manager Christian Hahn put it in this recent Discover Sitecore YouTube video:

“…[the] Cloud SDK is not another Sitecore SDK–it is the Sitecore SDK.”

It’s safe to assume that, eventually, the Sitecore Cloud SDK will be the only Sitecore SDK developers need to include in their head applications to integrate with any other Sitecore DXP offerings (which will be nice 👌).

ℹ For the remainder of this post, assume that a pre-0.5 version of the Cloud SDK is in use, say, 0.3.0—any version that doesn’t include search widgets (such that the Search JS SDK for React is still required).

Sitecore Search JS SDK for React 🔍

The Search JS SDK for React allows developers to create components such as search bars, search results components, etc. These components interact with search sources defined and indexed in Sitecore Search. You can read the official documentation here. While the latest version of the Cloud SDK includes some search dependencies, for older Next.js applications using older versions of the Cloud SDK, the Search JS SDK for React can still be used to build search interfaces.

The Search JS SDK for React uses a cookie to track events context called __ec (reference). This SDK is historically based on Sitecore Discover whose cookies are similarly documented here, e.g., __rutma.

ℹ For the remainder of this post, assume that version 2.5.5 of the Search JS SDK for React is in use.

The Problem 🙅‍♂️

Let’s say your XM Cloud project leverages JSS for Next.js, including the multisite add-on. This add-on (which is included in the official starter kit by default) allows a single Next.js application to drive multiple headless sites. Next, let’s assume that some of these sites operate outside of the United States and are potentially subject to different data protection and privacy laws. Finally, let’s assume that not all of the sites will use the full feature set from these SDKs. For example, what if a couple of the sites are small and don’t need to integrate with Sitecore Search at all?

How do you disable the cookies written to the browser when the Search SDK’s <WidgetsProvider> component is initialized? Even though the smaller sites aren’t using search widgets on any of their pages, the <WidgetsProvider> component is (usually) included in the Layout.tsx file and is still initialized. We don’t want to remove the component since other sites do use search widgets and require the <WidgetsProvider> component.

Can these SDKs be configured to (conditionally) not create cookies on the client browser?

The Solution ✅

🚨 First and foremost (before we get into how to disable cookies used by these SDKs), know that you must ensure that your application is compliant with any and all data privacy and data protection laws to which it is subject. This includes allowing users to opt-out of all browser cookies. Cookie preferences, their management, third-party solutions, GDPR, CCPA, etc. are all great topics but are well outside the scope of this post. To get started, refer to Sitecore’s documentation on data privacy to understand who is responsible for what when building an XM Cloud application.

With that small disclaimer out of the way, the programmatic hooks discussed in the sections below can be used in conjunction with whatever cookie management solution that makes sense for your application. Let’s assume that, for these smaller sites operating in different geographies that require neither CDP nor search, we just want to disable cookies from these SDKs altogether.

To disable Cloud SDK cookies:

The short version: just don’t call the SDK’s init() function 😅. One way this can be done is to add an environment variable and check its value within the .\src\<rendering-host>\src\lib\context\sdk\events.ts file and either return early or throw before the call to Events.init():

import * as Events from '@sitecore-cloudsdk/events/browser';
import { SDK } from '@sitecore-jss/sitecore-jss-nextjs/context';

const sdkModule: SDK<typeof Events> = {
  sdk: Events,
  init: async (props) => {
    // Events module can't be initialized on the server side
    // We also don't want to initialize it in development mode
    if (typeof window === 'undefined')
      throw 'Browser Events SDK is not initialized in server context';
    if (process.env.NODE_ENV === 'development')
      throw 'Browser Events SDK is not initialized in development environment';
    // We don't want to initialize if the application doesn't require it
    if (process.env.DISABLE_CLOUD_SDK === 'true') // <===== HERE
      throw 'Browser Events SDK is not initialized for this site';

    await Events.init({
      siteName: props.siteName,
      sitecoreEdgeUrl: props.sitecoreEdgeUrl,
      sitecoreEdgeContextId: props.sitecoreEdgeContextId,
      // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com"
      cookieDomain: window.location.hostname.replace(/^www\./, ''),
      // Cookie may be created in personalize middleware (server), but if not we should create it here
      enableBrowserCookie: true,
    });
  },
};

export default sdkModule;

By not calling Events.init(), the cookies aren’t written to the browser.

📝 Note that in newer versions of the XM Cloud starter kit using the Cloud SDK, the initialize function may be in the Bootstrap.tsx file; however, the same principle applies—don’t call the initialize() function by either returning early or setting up conditions such that the function is never called.

For consistency, assuming your application uses the OOTB CdpPageView.tsx component, you’d probably want to do something similar within that component. By default, page view events are turned off when in development mode. Simply add another condition to ensure that the return value of disabled() is true:

import {
  CdpHelper,
  LayoutServicePageState,
  useSitecoreContext,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { useEffect } from 'react';
import config from 'temp/config';
import { context } from 'lib/context';

/**
 * This is the CDP page view component.
 * It uses the Sitecore Cloud SDK to enable page view events on the client-side.
 * See Sitecore Cloud SDK documentation for details.
 * https://www.npmjs.com/package/@sitecore-cloudsdk/events
 */
const CdpPageView = (): JSX.Element => {
  ...
  /**
   * Determines if the page view events should be turned off.
   * IMPORTANT: You should implement based on your cookie consent management solution of choice.
   * By default it is disabled in development mode
   */
  const disabled = () => {
    return process.env.NODE_ENV === 'development' || process.env.DISABLE_CLOUD_SDK === 'true'; // <===== HERE
  };
  ...
  return <></>;
};

export default CdpPageView;

To disable Search JS SDK for React (Sitecore Discover) cookies:

The <WidgetsProvider> component (imported from @sitecore-search/react) includes a property named trackConsent (documented here) and it controls exactly that—whether or not tracking cookies related to visitor actions are created. Setting the value of this property to false disables the various cookies. In the Layout.tsx file, assuming we added another environment variable, the code would look something like this:

ata-enlighter-language="typescript">/**
 * This Layout is needed for Starter Kit.
 */
import React from 'react';
...
import { Environment, WidgetsProvider } from '@sitecore-search/react';

const Layout = ({ layoutData, headLinks }: LayoutProps): JSX.Element => {
  ...
  return (
    <>
      ...
        <div className="App">
          <WidgetsProvider
            env={process.env.NEXT_CEC_APP_ENV as Environment}
            customerKey={process.env.NEXT_CEC_CUSTOMER_KEY}
            apiKey={process.env.NEXT_CEC_API_KEY}
            publicSuffix={true}
            trackConsent={!(process.env.DISABLE_TRACK_CONSENT === 'true') /* <===== HERE */}
          >
            ...
          </WidgetsProvider>
        </div>
      ...
    </>
  );
};

export default Layout;

If trackConsent is false, then the various __r… cookies are not written to the browser.

⚠ It’s worth mentioning that, by default, trackConsent is true. To opt-out of cookies, developers must set the property to false.

 

Whether you control the use of cookies by using environment variables as described in this post or by integrating a more complex cookie preference and consent management system, the onus is on you and your XM Cloud head application to avoid using cookies without a user’s consent.

Thanks for the read! 🙏

]]>
https://blogs.perficient.com/2025/03/12/disabling-cookies-in-sitecore-sdks/feed/ 2 378419
Boost Sitecore Performance with Vercel Fluid Compute https://blogs.perficient.com/2025/03/10/boost-sitecore-performance-with-vercel-fluid-compute/ https://blogs.perficient.com/2025/03/10/boost-sitecore-performance-with-vercel-fluid-compute/#comments Mon, 10 Mar 2025 18:02:17 +0000 https://blogs.perficient.com/?p=378307

Are you using Vercel to supercharge your Sitecore experience? If so, you’re already benefiting from a powerful, globally optimized platform designed for modern web applications. But did you know you can take your performance even further? Vercel Fluid Compute is a game-changer, optimizing workloads for higher efficiency, lower costs, and enhanced scalability—perfect for high-performance Sitecore deployments.

What is Vercel Fluid Compute?

Fluid Compute is Vercel’s next-generation execution model, blending the best of serverless and traditional compute. Unlike conventional serverless architectures, which often suffer from cold starts and limited concurrency, Fluid Compute allows multiple requests to be processed within a single function instance. This leads to reduced latency, faster response times, and better resource utilization.

Why Sitecore Developers Should Care

Sitecore is a powerful digital experience platform, but ensuring smooth, high-speed performance at scale can be challenging. Fluid Compute helps reduce performance bottlenecks and optimize infrastructure costs, making it a perfect fit for Sitecore-powered applications. Here’s how it benefits you:

  • Faster Load Times: By reusing function instances and reducing cold starts, Fluid Compute improves Sitecore’s response times, ensuring users get the content they need—fast.
  • Cost Savings: Efficient resource usage can reduce compute costs by up to 85%, a significant reduction for enterprises managing high-traffic Sitecore applications.
  • Scalability Without Hassle: Fluid Compute dynamically scales functions based on demand, ensuring seamless performance even during traffic spikes.
  • Better Background Processing: Features like waitUntil enable asynchronous tasks such as logging and analytics to run without delaying user responses.

How Fluid Compute Compares

To truly understand the advantages of Fluid Compute for Sitecore, take a look at this comparison chart:

Vercelfluidcompute

As shown, Fluid Compute outperforms both traditional servers and serverless architectures in key areas such as scaling, concurrency, and cost efficiency. By preventing cold starts, enabling efficient auto-scaling, and optimizing resource usage, Fluid Compute ensures your Sitecore application runs at peak performance with minimal overhead.

How to Enable Fluid Compute for Sitecore on Vercel

One of the best aspects of Fluid Compute is how easy it is to implement.

  1. Deploy Your Sitecore-Powered App to Vercel – Ensure your Sitecore front-end is running on Vercel’s platform.
  2. Enable Fluid Compute – Simply update your Vercel project settings to opt into Fluid Compute for serverless functions.
  3. Enjoy Enhanced Performance – With zero additional configuration, your Sitecore app now benefits from better efficiency, lower costs, and higher scalability.

The Future of Sitecore Performance

As brands continue to push the boundaries of digital experiences, having a highly optimized, scalable compute model is essential. With Vercel Fluid Compute, Sitecore developers can future-proof their applications, ensuring exceptional performance while keeping costs in check.

]]>
https://blogs.perficient.com/2025/03/10/boost-sitecore-performance-with-vercel-fluid-compute/feed/ 1 378307
Lessons from the Front: Mapping Bootstrap Grid Parameters https://blogs.perficient.com/2025/03/06/lessons-from-the-front-mapping-bootstrap-grid-parameters/ https://blogs.perficient.com/2025/03/06/lessons-from-the-front-mapping-bootstrap-grid-parameters/#respond Thu, 06 Mar 2025 17:35:27 +0000 https://blogs.perficient.com/?p=378087

Intro 📖

While working on a recent project to migrate a Sitecore 9.1 solution to Sitecore XM Cloud, an interesting situation arose with Bootstrap grid parameters. More specifically, with grid rendering parameters configured on renderings migrated from the legacy 9.1 content tree into the XM Cloud content tree.

To start, some (brief?) background on SXA and the grid system. SXA includes several grid systems out-of-the-box (reference), including Bootstrap 4 (BS4) and Bootstrap 5 (BS5). These grid systems allow developers to build responsive layouts in a consistent, predictable way. When adding a new headless site in XM Cloud, BS5 is the default grid system (reference). The supporting Sitecore items for the different grid systems can be found in the content tree under /sitecore/system/Settings/Feature/Experience Accelerator (BS4 and BS5 are highlighted in the screenshot below, but there are others):

Bootstrap entries in the Sitecore content tree.

The grid system(s) a site uses can be configured on the site’s Settings item, e.g., /sitecore/content/Test Site Collection/Headless Site A/Settings:

Headless Site Grid Settings

When editing a rendering on a page in the Content Editor, Experience Editor, or XM Cloud Pages (which was recently updated 🎉), content authors can configure the grid and set things like column sizes, offsets, order, etc.:

Grid Rendering Parameters

These grid settings are stored as a single rendering parameter named GridParameters associated to the parent rendering in the page’s Renderings and/or Final Renderings field. For example, assuming a Container rendering has the Size settings depicted in the animation above (meaning: Mobile | Size | 12), the raw value of the rendering in presentation details would look something like this:

<r uid="{FE9A8A21-02C7-4EB0-B1B1-DF67ADD29ADD}"
  ...
  s:par="?Styles=%7b6221E315-B00F-4832-9054-B46F347EC247%7d&amp;GridParameters=%7b7465D855-992E-4DC2-9855-A03250DFA74B%7d&amp;DynamicPlaceholderId=1"
  s:ph="headless-main" />

The s:par attribute stores a URL-encoded query string containing the rendering parameters. The value of GridParameters is “%7b7465D855-992E-4DC2-9855-A03250DFA74B%7d”, or, when URL-decoded, “{7465D855-992E-4DC2-9855-A03250DFA74B}”. That looks a lot like a Sitecore item ID, right 😉?

📝 Note that, if there was more than one grid parameter, GridParameters would be a pipe-delimited (“|”) string of IDs.

The ID points to the grid size definition item located at (in this case, for BS5) /sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 5/Bootstrap 5 Grid Definition/Extra small/Size/12. Finally, as part of the definition of the size item, the applicable Bootstrap CSS class is specified in the Class field. It is this class (or classes, if more than one grid parameter is specified) that is applied to the markup when the component is rendered in the UI.

Bootstrap 5 Size 12

For a slightly more “real world” example, assume a Container rendering had (BS5) grid settings that looked like this:

Sample Bootstrap 5 Settings

The value of the GridParameters rendering parameter would be:

<r uid="{FE9A8A21-02C7-4EB0-B1B1-DF67ADD29ADD}"
  ...
  s:par="GridParameters=%7B7465D855-992E-4DC2-9855-A03250DFA74B%7D%7C%7B597223E6-B7EE-4D86-BC89-F7DA3DE8A7E5%7D%7C%7B7D865A50-F089-421E-ACDC-620DB03BC49D%7D&amp;FieldNames&amp;BackgroundImage&amp;Styles=%7B6221E315-B00F-4832-9054-B46F347EC247%7D&amp;RenderingIdentifier&amp;CSSStyles&amp;DynamicPlaceholderId=1"
  s:ph="headless-main" />

Which, URL-decoded would be: “{7465D855-992E-4DC2-9855-A03250DFA74B}|{597223E6-B7EE-4D86-BC89-F7DA3DE8A7E5}|{7D865A50-F089-421E-ACDC-620DB03BC49D}”.

The JSON pulled from the layout service for the rendering would look like this:

...
"uid": "fe9a8a21-02c7-4eb0-b1b1-df67add29add",
"componentName": "Container",
"dataSource": "",
"params": {
  "GridParameters": "col-12 col-md-10 col-xl-5", // <=== HERE
  "Styles": "container",
  "DynamicPlaceholderId": "1",
  "FieldNames": "Default"
}
...

And, finally, the resulting markup would look like this:

<div class="col-12 col-md-10 col-xl-5">
...
</div>

For the remainder of this post, assume the following:

  • The legacy 9.1 solution used BS4.
  • The XM Cloud solution used BS5 (but supported BS4).
  • The legacy 9.1 renderings were migrated to XM Cloud (and converted to JSON renderings).
  • The migrated renderings were implemented and appear as expected in the head application UI, complete with the expected/correct CSS grid classes.

The Problem 🙅‍♂️

Okay, so, what’s the issue? We have BS4 and BS5 available in XM Cloud and the migrated renderings are emitting the correct grid styles in the markup (e.g., col-12). What’s the problem?

The problem came when content authors went to view and/or update the grid style parameters, either in the Content Editor, Experience Editor, or XM Cloud Pages. There wasn’t an error or anything, but nothing appeared in the Grid tab or the Advanced tab when editing the rendering parameters for the rendering, as if the parameters weren’t there…🤔.

When viewing raw values of the Renderings and Final Renderings fields on the page, the GridParameters parameter was clearly present–so why wasn’t anything appearing in the editing UI for content authors?

The Cause 🐛

As you may have already guessed (and, yes, it seems obvious now), the reason for this was that the item IDs referenced in the grid rendering parameter are different between BS4 and BS5, even for two grid size definition items with the same CSS class. For example, the col-12 item for BS4 (/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 4/Bootstrap 4 Grid Definition/Extra small/Size/12) didn’t have the same ID as the matching col-12 item in BS5 (/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 5/Bootstrap 5 Grid Definition/Extra small/Size/12).

Because the new XM Cloud solution had both the supporting BS4 and BS5 items available under /sitecore/system/Settings/Feature/Experience Accelerator, the BS4 grid CSS classes were still coming through in the UI. However, when editing, the rendering parameters modal was expecting the BS5 IDs (since the site was configured to use BS5) to bind the editing controls. Since no BS5 parameter IDs were found, the editing controls in the modal weren’t bound to the correct values and appeared broken for content authors.

The Solution ✅

As usual, Sitecore PowerShell Extensions (SPE) to the rescue. A PowerShell script was written to iterate over page renderings, map the Bootstrap 4 parameter IDs to their equivalent Bootstrap 5 parameter IDs based on a CSS class name match, if possible, and then save the updated rendering parameters back to the page.

⚠ In some cases, it wasn’t possible to map parameters between the two grid systems as there are breaking changes between BS4 and BS5. In BS4, for example, some Order definitions exist that do not exist in BS5 i.e., /sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 4/Bootstrap 4 Grid Definition/Extra small/Order/6.

Luckily, in the case of this particular project, the vast majority of the parameters did have a BS5 equivalent and were remapped accordingy, allowing the rendering parameters modal to properly reflect the current grid parameters for content authors. ✨

The Script ⚙

Update-GridParameters.ps1

A few highlights on the script and how it works:

    1. The script is intended to be executed via the PowerShell ISE interface in Sitecore.
    2. Update the -Path parameter according to where your content pages live in the tree; I’d recommend running the script on a subset of items initially.
    3. The script emits a report using the Show-ListView command that enumerates which renderings and parameters were updated (or that won’t be updated); the report can be exported if you need to, say, attach it to a work item in Azure DevOps or JIRA.
    4. If a grid rendering parameter ID cannot be mapped to a BS5 ID or if the candidate ID isn’t a BS4 ID (including if it’s already a BS5 ID), then it is skipped but still appears in the report.
    5. Keep the -WhatIf switch set until you’re ready to apply the updates.

Here’s the script:

Function Update-GridParameters {
    <#
    .SYNOPSIS
        Maps Bootstrap 4 grid rendering parameters to Bootstrap 5 grid rendering parameters.

    .DESCRIPTION
        This function maps Bootstrap 4 grid rendering parameter IDs to their Bootstrap 5 equivalent rendering parameter IDs, if possible.

    .NOTES
        Nick Sturdivant | Perficient | nick.sturdivant@perficient.com | https://www.linkedin.com/in/nicksturdivant/
    #>

    param
    (
        [Parameter()]
        [String] $Path,

        [Parameter()]
        [Switch] $WhatIf
    )

    BEGIN {
        Write-Host "Beginning $($MyInvocation.MyCommand)"
    }

    PROCESS {
        # get bootstrap 4 grid parameter definitions
        $bs4GridParameters = Get-ChildItem -Path "/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 4/Bootstrap 4 Grid Definition" -Recurse `
        | Where-Object {
            $_.TemplateID -eq "{CB3B3906-BE80-4D9B-A2A4-038193DA5422}" # /sitecore/templates/Foundation/Experience Accelerator/Grid/Grid Definition Items/Class
        }

        # get bootstrap 5 grid parameter definitions
        $bs5GridParameters = Get-ChildItem -Path "/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 5/Bootstrap 5 Grid Definition" -Recurse `
        | Where-Object {
            $_.TemplateID -eq "{CB3B3906-BE80-4D9B-A2A4-038193DA5422}" # /sitecore/templates/Foundation/Experience Accelerator/Grid/Grid Definition Items/Class
        }

        # build mapping
        $bs4To5Mappings = @()
        foreach ($bs4GridParameter in $bs4GridParameters) {
            # match bootstrap 5 parameter based on class name
            $bs5GridParameter = $bs5GridParameters | Where-Object { $_.Fields["Class"].Value -eq $bs4GridParameter.Fields["Class"].Value }

            if ($null -ne $bs5GridParameter) {
                $bs4To5Mappings += [PSCustomObject]@{
                    BS4Id    = $bs4GridParameter.ID
                    BS4Class = $bs4GridParameter.Fields["Class"].Value
                    BS5Id    = $bs5GridParameter.ID
                    BS5Class = $bs5GridParameter.Fields["Class"].Value
                }
            }   
        }

        $report = @()

        Write-Host "Processing $Path..." -ForegroundColor Cyan
    
        # get children based on path parameter, filtering to only those items with a rendering defined in their final layout
        $items = Get-ChildItem -Path $Path -Recurse | Where-Object { ($null -ne (Get-Rendering -Item $_ -FinalLayout)) }
    
        foreach ($item in $items) {
            $renderings = Get-Rendering -Item $item -FinalLayout

            if ($null -ne $renderings) {
                foreach ($rendering in $renderings) {
                    # get grid rendering parameter
                    $renderingParameter = Get-RenderingParameter -Rendering $rendering -Name "GridParameters"

                    if ($null -ne $renderingParameter) {
                        Write-Host "Processing grid rendering parameters on item $($item.ID), rendering $($rendering.UniqueId)..." -ForegroundColor Green
                        $gridParametersIdString = $renderingParameter["GridParameters"]

                        if (-not [string]::IsNullOrWhiteSpace($gridParametersIdString)) {
                            $gridParameterIds = $gridParametersIdString.Split("|")
                            $newGridParameterIds = @()

                            foreach ($gridParameterId in $gridParameterIds) {
                                # check if the parameter is bootstrap 4
                                $mappedParameter = $bs4To5Mappings | Where-Object { $_.BS4Id.ToString() -eq $gridParameterId } | Select-Object -First 1

                                if ($null -eq $mappedParameter) {   
                                    $message = "Grid parameter ID $gridParameterId is not a Bootstrap 4 parameter that can be mapped to a Bootstrap 5 parameter, retaining original ID."
    
                                    Write-Host $message -ForegroundColor Yellow
    
                                    $report += @{
                                        ItemId                 = $item.ID
                                        ItemPath               = $item.FullPath
                                        RenderingItemId        = $rendering.ItemID
                                        RenderingUniqueId      = $rendering.UniqueId
                                        CurrentGridParameterId = $gridParameterId
                                        MappedGridParameterId  = "N/A"
                                        GridClassName          = "N/A"
                                        Notes                  = $message
                                    }

                                    $newGridParameterIds += $gridParameterId

                                    # process next parameter
                                    continue
                                }
    
                                # map to bootstrap 5 parameter
                                $report += @{
    
                                    ItemId                 = $item.ID
    
                                    ItemPath               = $item.FullPath
    
                                    RenderingItemId        = $rendering.ItemID
    
                                    RenderingUniqueId      = $rendering.UniqueId
    
                                    CurrentGridParameterId = $gridParameterId
    
                                    MappedGridParameterId  = $mappedParameter.BS5Id.ToString()
    
                                    GridClassName          = $mappedParameter.BS5Class
    
                                    Notes                  = "Mapping Bootstrap 4 grid parameter ID $gridParameterId to Bootstrap 5 grid parameter ID $($mappedParameter.BS5Id.ToString())."
    
                                }
    
                                $newGridParameterIds += $mappedParameter.BS5Id.ToString()   
                            }
    
                            $newGridParametersIdString = $newGridParameterIds -join "|"
    
                            Write-Host "Current: $gridParametersIdString"
    
                            Write-Host "Mapped: $newGridParametersIdString"
    
                            if ($gridParametersIdString -eq $newGridParametersIdString) {   
                                Write-Host "Current and mapped grid parameters match, skipping update..." -ForegroundColor Yellow
                                
                                # process next rendering
                                continue
                            }
    
                            # set new rendering parameter   
                            $setMessage = "Setting new grid parameters on item $($item.ID) ($($item.FullPath)), rendering $($rendering.UniqueId)..."
    
                            Write-Host $setMessage -ForegroundColor Yellow
    
                            $newGridParameters = [Ordered]@{"GridParameters" = $newGridParametersIdString }
    
                            $report += @{
                                ItemId                 = $item.ID
                                ItemPath               = $item.FullPath
                                RenderingItemId        = $rendering.ItemID
                                RenderingUniqueId      = $rendering.UniqueId
                                CurrentGridParameterId = $gridParametersIdString
                                MappedGridParameterId  = $newGridParametersIdString
                                GridClassName          = ""
                                Notes                  = "$setMessage ($gridParametersIdString ==> $newGridParametersIdString)"
                            }

                            if (-not $WhatIf) {
                                # update new grid parameters and then update the rendering
                                $rendering | Set-RenderingParameter -Parameter $newGridParameters | Set-Rendering -Item $item -FinalLayout
                            }
                        }
                    }
                }
            }
        }
    
        # display report
        $report |
        Show-ListView -Property @{ Label = "Item ID"; Expression = { $_.ItemId } },
        @{Label = "Item Path"; Expression = { $_.ItemPath } },
        @{Label = "Rendering Item ID"; Expression = { $_.RenderingItemId } },
        @{Label = "Rendering Unique ID"; Expression = { $_.RenderingUniqueId } },
        @{Label = "Current Grid Parameter ID"; Expression = { $_.CurrentGridParameterId } },
        @{Label = "Mapped Grid Parameter ID"; Expression = { $_.MappedGridParameterId } },
        @{Label = "Grid Class Name"; Expression = { $_.GridClassName } },
        @{Label = "Notes"; Expression = { $_.Notes } }
    }

    END {
        Write-Host "Ending $($MyInvocation.MyCommand)"
    }
}

# 1. Change the Path parameter as necessary, depending on where the content pages are in the tree.
# 2. Use the -WhatIf switch to preview the changes; remove the switch to update the renderings and pages.
Update-GridParameters -Path "/sitecore/content/Test Site Collection/Headless Site A" -WhatIf

Bonus Script 🌟

My colleague (and Sitecore MVP) Eric Sanner happened to write a similar script recently. I can neither confirm nor deny that the two of us wrote two scripts to do the same thing at around the same time 😅. His script can be found below and illustrates another approach to solve the same problem. Thanks for sharing, Eric!

BS4toBS5.ps1

<#
    .SYNOPSIS
       Update Final Layouts
        
    .DESCRIPTION
        Find bootstrap4 grid settings in final layout and convert to bootstrap5
        
    .NOTES  
        Eric Sanner | Perficient | eric.sanner@perficient.com | https://www.linkedin.com/in/ericsanner/      
#>

#BEGIN Config
$database = "master"
$allowDelete = $false
#END Config

#BEGIN Helper Functions

function Write-LogExtended {
    param(
        [string]$Message,
        [System.ConsoleColor]$ForegroundColor = $host.UI.RawUI.ForegroundColor,
        [System.ConsoleColor]$BackgroundColor = $host.UI.RawUI.BackgroundColor
    )

    Write-Log -Object $message
    Write-Host -Object $message -ForegroundColor $ForegroundColor -BackgroundColor $backgroundColor
}

function Strip-Html {
    #https://www.regular-expressions.info/lookaround.html#lookahead Replaces multiple spaces with a single space
    param (
        [string]$text
    )
    
    $text = $text -replace '<[^>]+>',' '
    $text = $text -replace " (?= )", "$1"
    $text = $text.Trim()
    
    return $text
}

function Truncate-Output {
    param (
        $obj,
        $maxLeng
    )
    
    $ret = "";
    
    if($obj -ne $null)
    {
        $str = $obj.ToString().Trim()
        $leng = [System.Math]::Min($str.Length, $maxLeng)
        $truncated = ($str.Length -gt $maxLeng)
        
        $ret = $str.Substring(0, $leng)
        if($truncated -eq $true)
        {
            $ret = $ret + "..."
        }
    }

    return $ret
}

#END Helper Functions

#BEGIN Sitecore Functions

function Get-SitecoreItemById {
    param(
        [string]$id
    )

    return Get-Item -Path $database -ID $id -ErrorAction SilentlyContinue
}

function Update-SitecoreItem {
    param(
        [Sitecore.Data.Items.Item]$item,
        [System.Collections.Hashtable]$updates
    )
    
    if($item -eq $null)
    {
        Write-LogExtended "[E] Error updating item $($item) - Item is null" Red
        return
    }
    
    if($updates -eq $null)
    {
        Write-LogExtended "[E] Error updating item $($item) - Update hashtable is null" Red
        return
    }
        
    $changeDetected = $false
    $foregroundColor = "Green"
    
    Write-LogExtended "[I] Updating Item $($item.ID) - $($item.Name)"
    $item.Editing.BeginEdit()
    
    foreach($key in $updates.GetEnumerator())
    {
        if($item.($key.Name) -ne $null)
        {
            $output = "Field Name '$($key.Name)' Current Value: '$(Truncate-Output $item.($key.Name) 40)' New Value: '$(Truncate-Output $key.Value 40)'"            
            
            if($item.($key.Name) -ne $key.Value)
            {
                Write-LogExtended "[U] $($output)"
                $item.($key.Name) = $key.Value
                $changeDetected = $true
            }
            else
            {
                Write-LogExtended "[-] $($output)"
            }
        }
    }
    
    $itemModified = $item.Editing.EndEdit()
    
    if($changeDetected -ne $itemModified)
    {
        $foregroundColor = "Red"
    }
    
    Write-LogExtended "[I] Change Detected: $($changeDetected) Item modified $($itemModified)" $foregroundColor
}
#END Sitecore Functions

#BEGIN Conversion Functions

function ProcessItem {
    param(
        [Sitecore.Data.Items.Item]$item     
    )
    
    Write-LogExtended "[I] Processing item $($item.ID): $($item.ItemPath)"

    $gridPattern = "GridParameters=%7B(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})%7D"
    $bs4Pattern = "Bootstrap 4"
    $finalRenderings = $item["__Final Renderings"]
    $finalRenderingsNew = $item["__Final Renderings"]
    $gridRenderings = @{}

    $gridMatches = Select-String -InputObject $finalRenderings -Pattern $gridPattern -AllMatches    
    foreach($gridMatch in $gridMatches.Matches)
    {       
        if(!$gridRenderings.ContainsKey("$($gridMatch.Groups[1].Value)"))
        {
            $gridRenderingItem = Get-SitecoreItemById $gridMatch.Groups[1].Value
            $gridRenderings["$($gridMatch.Groups[1].Value)"] = $gridRenderingItem.ItemPath          
        }
    }

    foreach($rendering in $gridRenderings.GetEnumerator())
    {
        $bs4Matches = Select-String -InputObject $rendering.Value -Pattern $bs4Pattern
        if($bs4Matches)
        {
            Write-LogExtended "[I] Found BS4 GridParams $($rendering.Value)" -ForegroundColor "Red"
            $bs4Items.Add($item.ID, $item.Path)

            if($bs4ToBs5Mapping.ContainsKey($rendering.Name))
            {
                Write-LogExtended "[I] Mapping $($rendering.Name) to $($bs4ToBs5Mapping[$rendering.Name])"  -ForegroundColor "Green"
                
                $finalRenderingsNew = $finalRenderingsNew -replace $rendering.Name, $bs4ToBs5Mapping[$rendering.Name]
            }
            else
            {
                Write-LogExtended "[E] No Mapping Found for $($rendering.Name)"  -ForegroundColor "Red"
            }
        }
    }   

    if($update -and $finalRenderings -ne $finalRenderingsNew)
    {
        $updates = @{}  
        $updates.Add("__Final Renderings", $finalRenderingsNew)

        Update-SitecoreItem $item $updates
    }
}

#END Conversion Functions

#BEGIN Main

    $update = $true
    $bs4Items = @{}

    $bs4ToBs5Mapping = @{}
    #/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 4/Bootstrap 4 Grid Definition/Extra small/Size/12 -> /sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 5/Bootstrap 5 Grid Definition/Extra small/Size/12
    $bs4ToBs5Mapping.Add("908E2BC6-C110-4ED7-AF39-7EEACBB31A34", "7465D855-992E-4DC2-9855-A03250DFA74B") 
    #/sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 4/Bootstrap 4 Grid Definition/Extra small/Size/8 -> /sitecore/system/Settings/Feature/Experience Accelerator/Bootstrap 5/Bootstrap 5 Grid Definition/Extra small/Size/8
    $bs4ToBs5Mapping.Add("D65D90FB-45BF-4A04-A1EA-6F348E7CCBEA", "F2A11D85-8B09-40AC-B5D8-A9E1025F899D")    

    $parentPath = "master:/sitecore/content/<tenantName>/<siteName>/Home"
    $childItems = Get-ChildItem -Path $parentPath -Recurse

    $childItems | ForEach-Object {
        ProcessItem $_
    }

    Write-LogExtended "[I] Found $($bs4Items.Count) Items that reference BS4"
    
#END Main

Thanks for the read! 🙏

]]>
https://blogs.perficient.com/2025/03/06/lessons-from-the-front-mapping-bootstrap-grid-parameters/feed/ 0 378087