By admin • March 3, 2025
When working on a Sitecore XM Cloud project with multiple developers, managing Sitecore Serialization efficiently is crucial. One of the biggest challenges we faced was handling merge conflicts and ensuring that each developer could work independently on their components without overwriting others' work.
In this blog post, I’ll share how we structured our serialization strategy to allow multiple developers to work on separate components without conflicts or accidental data loss, while still keeping all components organized under a single "Page Content" folder in Sitecore Pages.
The Challenge: Merge Conflicts & Data Loss
In a typical Sitecore Serialization setup, we would have a single module.json
for "Page Content", containing all the components under it. However, this introduced a major issue:
Problems We Faced
- Merge Conflicts – When multiple developers work on different components, changes in
module.json
often led to merge conflicts in Git. - Accidental Data Removal – If one developer serialized and pushed a module, it could remove changes made by another developer who was working in the same module.
- Environment Deployment Issues – Developers wanted to push only their own component changes to the dev environment without affecting others.
- Lack of Structure in Sitecore Pages – If each component had its own module, they would appear as separate sections in Sitecore Pages instead of being neatly grouped together.
To solve this, we decided to modularize each component separately while keeping them logically grouped under "Page Content" in Sitecore Pages.
The Solution: Separate Modules for Each Component, But Under One "Page Content" Folder
Instead of having one module.json
for all Page Content components, we structured our serialization like this:
Folder Structure: Each Component in Its Own Module, Inside "Page Content"
src/project/mysite/sitecore/
│── PageContent/
PromoCTA.tsx
Banner.tsx
Carousel.tsx
- Each component has its own
module.json
, so developers work independently. - No merge conflicts because no one modifies a shared
module.json
. - Each developer can push only their component’s changes without overwriting others.
- All components are still grouped under "Page Content" in Sitecore Pages.
Example module.json
for Each Component
Each component gets its own module.json
, but the path ensures all components still appear under "Page Content" in Sitecore Pages.
Example: PromoCTA.module.json
{
"$schema": "../../../.sitecore/schemas/ModuleFile.schema.json",
"namespace": "PromoCTA",
"references": ["Feature.Folders"],
"items": {
"path": "~/authoring/items/PromoCTA",
"includes": [
{
"name": "PromoCTATemplates",
"path": "/sitecore/templates/Feature/MyProject/PromoCTA",
"scope": "ItemAndDescendants",
"allowedPushOperations": "CreateUpdateAndDelete"
},
{
"name": "PromoCTARendering",
"path": "/sitecore/layout/Renderings/Feature/MyProject/Page Content",
"scope": "DescendantsOnly",
"rules": [
{
"path": "/PromoCTA",
"scope": "SingleItem",
"allowedPushOperations": "CreateUpdateAndDelete"
},
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "PromoCTARenderingThumbnails",
"path": "/sitecore/media library/Feature/MyProject/Rendering Thumbnails/PromoCTA",
"scope": "ItemAndDescendants",
"allowedPushOperations": "CreateOnly"
},{
"name": "PromoCTARenderingParametersTemplates",
"path": "/sitecore/templates/Feature/MyProject/RenderingParameters/PromoCTA",
"scope": "ItemAndDescendants",
"allowedPushOperations": "CreateUpdateAndDelete"
}
]
}
}
The Benefits of This Approach
No Merge Conflicts – Developers no longer modify the same module.json, avoiding unnecessary conflicts in Git.
Independent Workflows – Each developer can work on their own component without affecting others.
Controlled Deployments – Developers can push only their new changes to the dev environment without overriding colleagues’ work.
Logical Organization in Sitecore – Despite separate modules, all components still appear under "Page Content" in Sitecore Pages, keeping the structure clean and intuitive for editors.
Features.Folders module.json
To ensure a consistent folder structure, we created a Features.Folders module that holds all necessary folders for working with this approach.
{
"$schema": "../../.sitecore/schemas/ModuleFile.schema.json",
"namespace": "Feature.Folders",
"references": ["Project","Foundation.*"],
"items": {
"includes": [
{
"name": "Feature-TemplatesFolder",
"path": "/sitecore/templates/Feature/",
"scope": "itemAndDescendants",
"rules": [
{
"path": "/MyProject",
"scope": "SingleItem",
"allowedPushOperations": "CreateOnly"
},
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "Feature-RenderingsFolder",
"path": "/sitecore/layout/Renderings/Feature/MyProject",
"scope": "itemAndDescendants",
"allowedPushOperations": "CreateOnly",
"rules": [
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "Feature-RenderingsPageContentFolder",
"path": "/sitecore/layout/Renderings/Feature/MyProject/Page Content",
"scope": "itemAndDescendants",
"allowedPushOperations": "CreateOnly",
"rules": [
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "Feature-PlaceholderSettingsFolder",
"path": "/sitecore/layout/Placeholder Settings/Feature/MyProject",
"scope": "itemAndDescendants",
"allowedPushOperations": "CreateOnly",
"rules": [
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "Feature-MediaMyProjectFolder",
"path": "/sitecore/Media Library/Feature/MyProject",
"scope": "itemAndDescendants",
"allowedPushOperations": "CreateOnly",
"rules": [
{
"path": "/Rendering Thumbnails",
"scope": "SingleItem",
"allowedPushOperations": "CreateOnly"
},
{
"path": "*",
"scope": "Ignored"
}
]
},
{
"name": "Feature-RenderingParameters",
"path": "/sitecore/templates/Feature/MyProject/RenderingParameters",
"scope": "SingleItem",
"allowedPushOperations": "CreateOnly"
}
]
}
}