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 (
-
+