We’re encountering an issue with a component(which is added in sitecore cms and has html,css,scripts) in our Sitecore Headless (Next.js) implementation.
In Sitecore XP, we were able to use Rich Text fields to inject HTML, CSS, inline scripts, and external scripts — all of which executed seamlessly on the client side.
However, in the current headless setup, even when using <RichText /> to render content from Sitecore, the scripts (especially inline and external ones) are not executing at runtime.
We’ve attempted three different implementation strategies to fix this, but still face two major problems:
1.Scripts don’t consistently load — particularly on second visits
2.Initial script load takes 1–1.5 seconds, causing a noticeable performance hit
Has anyone faced a similar issue or found a reliable solution for dynamically injecting html,css,script content(inline and external scripts) and executing from Sitecore in a Next.js environment?
We have tried in 3 ways
#Code version-1
import { useEffect, useRef } from 'react'; import { ComponentProps } from 'lib/component-props'; import { Field, RichText } from '@sitecore-jss/sitecore-jss-nextjs'; import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs'; import { formatString } from 'scripts/format-string'; interface CodeObjectProps extends ComponentProps { fields: { HtmlContent: Field<string>; InPageNavigationTitle: Field<string>; }; } const CodeObject = ({ fields }: CodeObjectProps): JSX.Element | null => { const { sitecoreContext } = useSitecoreContext(); const isExperienceEditor = sitecoreContext?.pageEditing ?? false; const navigationTitle = fields?.InPageNavigationTitle?.value; const scriptsLoaded = useRef(false); useEffect(() => { if (!isExperienceEditor && !scriptsLoaded.current) { const htmlContent = fields?.HtmlContent?.value || ''; const tempDiv = document.createElement('div'); tempDiv.innerHTML = htmlContent; const scriptTags = tempDiv.getElementsByTagName('script'); for (let i = 0; i < scriptTags.length; i++) { const scriptTag = scriptTags[i]; const newScript = document.createElement('script'); if (scriptTag.src) { newScript.src = scriptTag.src; newScript.defer = true; document.head.appendChild(newScript); } else { newScript.textContent = scriptTag.innerHTML; document.body.appendChild(newScript); } } scriptsLoaded.current = true; } }, [fields?.HtmlContent, isExperienceEditor]); return ( <RichText field={fields?.HtmlContent} tag="section" className="code-object-sec" {...(navigationTitle ? { id: formatString(navigationTitle) } : {})} {...(navigationTitle ? { inPageNav_Name: navigationTitle } : {})} /> ); }; export default CodeObject; #code version 2:
import { ComponentProps } from 'lib/component-props'; import { Field } from '@sitecore-jss/sitecore-jss-nextjs'; import Script from 'next/script'; import { parse } from 'node-html-parser'; import { formatString } from 'scripts/format-string'; interface CodeObjectProps extends ComponentProps { fields: { HtmlContent: Field<string>; InPageNavigationTitle: Field<string>; }; } const CodeObject = ({ fields }: CodeObjectProps): JSX.Element | null => { const htmlContent = fields?.HtmlContent?.value; const navigationTitle = fields?.InPageNavigationTitle?.value; if (!htmlContent) return null; const parsedHtml = parse(htmlContent); const scriptTags = parsedHtml.querySelectorAll('script'); // Strip out script tags from HTML scriptTags.forEach((tag) => tag.remove()); const nonScriptHtml = parsedHtml.toString(); return ( <> {/* Render non-script HTML */} <section className="code-object-sec" {...(navigationTitle ? { id: formatString(navigationTitle) } : {})} {...(navigationTitle ? { inPageNav_Name: navigationTitle } : {})} dangerouslySetInnerHTML={{ __html: nonScriptHtml }} /> {/* Inject scripts (external & inline) */} {scriptTags.map((scriptTag, index) => { const src = scriptTag.getAttribute('src'); const innerHtml = scriptTag.innerHTML?.trim(); if (src) { return ( <Script key={`external-${index}`} src={src} strategy="beforeInteractive" // Load early for dependencies /> ); } return ( <Script key={`inline-${index}`} id={`inline-script-${index}`} strategy="afterInteractive" dangerouslySetInnerHTML={{ __html: innerHtml ?? '' }} /> ); })} </> ); }; export default CodeObject; Any suggestions or best practices would be greatly appreciated!