diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/component-factory.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/component-factory.ts index e5e66c3458..b39326b10e 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/component-factory.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/component-factory.ts @@ -52,7 +52,7 @@ ${componentFiles const moduleName = removeDynamicModuleNameEnding(component.moduleName); return `const ${moduleName} = { module: () => import('${component.path}'), - element: () => dynamic(${moduleName}.module) + element: (isEditing?: boolean) => isEditing ? require('${component.path}')?.default : dynamic(${moduleName}.module) }`; } @@ -89,6 +89,10 @@ ${componentFiles // componentFactory uses 'dynamic(...)' because primary usage of it to render 'React Component' (default export). // See https://nextjs.org/docs/advanced-features/dynamic-import // At the end you will have single preloaded script for each lazy loading module. +// Editing mode doesn't work well with dynamic components in nextjs: dynamic components are not displayed without refresh after a rendering is added. +// This happens beacuse Experience Editor simply inserts updated HTML generated on server side. This conflicts with nextjs dynamic logic as no HTML gets rendered for dynamic component +// So we use require() to obtain dynamic components in editing mode while preserving dynamic logic for non-editing scenarios +// As we need to be able to seamlessly work with dynamic components in both editing and normal modes, different componentFactory functions will be passed to app export function componentModule(componentName: string) { const component = components.get(componentName); @@ -101,14 +105,14 @@ export function componentModule(componentName: string) { return component; } - -export function componentFactory(componentName: string, exportName?: string) { + +function baseComponentFactory(componentName: string, exportName?: string, isEditing: boolean) { const component = components.get(componentName); // check that component should be dynamically imported if (component?.element) { // return next.js dynamic import - return component.element(); + return component.element(isEditing); } if (exportName) { @@ -117,6 +121,14 @@ export function componentFactory(componentName: string, exportName?: string) { return component?.default || component; } + +export function componentFactory(componentName: string, exportName?: string) { + return baseComponentFactory(componentName, exportName, false); +} + +export function editingComponentFactory(componentName: string, exportName?: string) { + return baseComponentFactory(componentName, exportName, true); +} `; } diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx index 8755ce09ae..7c5301b5d9 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx @@ -13,7 +13,8 @@ import { } from '@sitecore-jss/sitecore-jss-nextjs'; import { SitecorePageProps } from 'lib/page-props'; import { sitecorePagePropsFactory } from 'lib/page-props-factory'; -import { componentFactory } from 'temp/componentFactory'; +// different componentFactory method will be used based on whether page is being edited +import { componentFactory, editingComponentFactory } from 'temp/componentFactory'; <% if (prerender === 'SSG') { -%> import { sitemapFetcher } from 'lib/sitemap-fetcher'; <% } -%> @@ -29,9 +30,14 @@ const SitecorePage = ({ notFound, componentProps, layoutData }: SitecorePageProp return ; } + const isEditing = layoutData.sitecore.context.pageEditing; + return ( - +