Layout Assignments
| Attribute | Type | Description | Package Types Applicable |
|---|---|---|---|
| ignoreLayoutAssignments | boolean | Strip UI-magnet metadata (action overrides, object-wide and per-recordType compactLayoutAssignment) from this package's *.object-meta.xml files at build time so the artifact can deploy without depending on UI components owned by another package. |
|
| applyLayoutAssignments | boolean | Re-applies layout assignments declared in mutators/layout-assignments.yml at deploy time, composing partial CustomObject and Profile metadata using the target org's envelope. Defaults to enabled when the YAML is present; set to false to opt out. |
|
In a Flxbl project, the package that owns a CustomObject is rarely the same one that owns the FlexiPages, Lightning record pages and CompactLayouts assigned to it. The CustomObject definition references those UI components by name, which means deploying the object package fails unless the UI package is already in the org. The two flags below split that dependency by stripping the references at build time in the object-owning package and re-applying them at deploy time from the UI package.
How it works
┌──────────────────────┐ ┌─────────────────────┐
│ Object package │ build time │ UI package │
│ ignoreLayoutAssign- │ ────── strip ─────► │ mutators/ │
│ ments: true │ │ layout-assignments. │
│ │ │ yml │
│ Account.object- │ │ │
│ meta.xml │ deploy time │ applyLayout- │
│ (no inline refs) │ ◄──── compose ──── │ Assignments: true │
└──────────────────────┘ └─────────────────────┘- Build time — when
ignoreLayoutAssignmentsistrue, sfp removes inline<actionOverrides>(Flexipage / Visualforce / LightningComponent), top-level<compactLayoutAssignment>and per-recordType<compactLayoutAssignment>from every*.object-meta.xmlin the package. The artifact deploys cleanly even if the UI package isn't yet in the org. The strip log lands in.sfpowerscripts/logs/<package>so build stdout stays tidy. - Deploy time — the UI package carries
mutators/layout-assignments.yml. At install time, sfp reads the YAML, fetches each object's envelope (label,pluralLabel,nameField,sharingModel,deploymentStatus, recordTypeactive/label) from the target org via REST/Tooling, and composes a partialCustomObject(and partialProfilefor layout assignments) that re-applies the references. Because the envelope is sourced from the live org, the deploy never overwrites object metadata. - Pull time — when
sfp pullretrieves into a package marked withignoreLayoutAssignments: true, the inline references are stripped again so source-tracking churn doesn't cycle on every pull.
YAML format
The YAML lives at mutators/layout-assignments.yml inside the UI package directory. It is schema-validated; typos and unknown keys cause the deploy to fail loudly rather than silently dropping entries.
# Layout-magnet metadata that this package OWNS but lives on objects
# and profiles defined in other packages. Composed at deploy time using
# the target org's envelope plus the assignments below.
objects:
Account:
# Object-wide compact layout (CustomObject's top-level
# <compactLayoutAssignment>)
compactLayout: Account_Default_Compact
# Replaces standard actions on the object
actionOverrides:
- actionName: View
type: Flexipage # Flexipage | Visualforce | LightningComponent
content: Account_Record_Page
formFactor: Large # optional
skipRecordTypeSelect: false # optional
# Per-recordType compact layout assignments
recordTypes:
Business:
compactLayout: Business_Compact
Personal:
compactLayout: Person_Account_Compact
profiles:
Sales_User:
# Profile <layoutAssignments>. Deployed as a partial Profile that
# merges additively — other profile sections (objectPermissions,
# userPermissions, ...) are untouched.
layoutAssignments:
- layout: Account-Business_Layout
recordType: Account.Business
- layout: Account-Personal_Layout
recordType: Account.PersonalSetting up an existing project
Use sfp project layout-assignments extract once per UI package to seed the YAML from inline assignments already in your source. The command refuses to overwrite an existing YAML — pass --force to confirm overwrite.
# Migration: pull existing inline assignments into a UI-owned YAML and
# strip them from the object package's source in one shot.
sfp project layout-assignments extract \
--source-package src/core-crm \
--target-package src/ui-crm \
--stripAfter the YAML exists, hand-edit it as the source of truth. Subsequent stripping should happen at build time via ignoreLayoutAssignments rather than re-running the extract command.
Sample sfdx-project.json
{
"packageDirectories": [
{
"path": "src/core-crm",
"package": "core-crm",
"versionNumber": "1.0.0.NEXT",
"type": "source",
"ignoreLayoutAssignments": true
},
{
"path": "src/ui-crm",
"package": "ui-crm",
"versionNumber": "1.0.0.NEXT",
"type": "source",
"applyLayoutAssignments": true
}
]
}What is and isn't handled
| Reference | Strip at build | Re-apply at deploy |
|---|---|---|
<actionOverrides> of type Flexipage / Visualforce / LightningComponent | ✓ | ✓ |
Object-wide <compactLayoutAssignment> | ✓ | ✓ |
Per-recordType <compactLayoutAssignment> | ✓ | ✓ |
Profile <layoutAssignments> | sfprofiles reconciler prunes them at deploy | ✓ (composed as partial Profile) |
<actionOverrides> of type Default (no content) | n/a — built-in Salesforce behaviour | n/a |
<recordTypes> definition itself (active, label, picklistValues, businessProcess, ...) | n/a — owned by the object package | n/a |
If the YAML references an object or recordType that isn't present in the target org, the modifier skips it with a warning rather than failing the deploy. Profile names that don't exist in the target org are likewise skipped.
The deploy-time composer uses the target org's envelope verbatim — label, pluralLabel, nameField, deploymentStatus, sharingModel for the CustomObject and active, label for each recordType. This means re-deploying the partial is idempotent and never overrides the live object's metadata.