Problem/Motivation
Component developers evolve their components over time: adding or removing props and slots, changing prop types, updating widgets, adjusting requiredness, or modifying prop expressions. Since component instances stored in Canvas pages, templates, and regions reference a specific Component config entity version (see #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions ⇒ less data to store per XB field row), a mechanism is needed to upgrade existing instances to newer versions when the component definition changes.
Without such a mechanism, content creators would be stuck on old widget UX, stale prop definitions, or orphaned data from removed props.
Proposed resolution
A ComponentInstanceUpdater plugin system was introduced that automatically upgrades component instances when a Canvas entity (page, template, region) is opened for editing. The system is conservative: it only auto-upgrades when the change has no destructive data storage implications.
Core infrastructure
ComponentInstanceUpdaterInterfacedefinesisUpdateNeeded(),canUpdate(), andupdate()methods. The primary implementation isGeneratedFieldExplicitInputUxComponentInstanceUpdater.- Auto-upgrade on edit: when a Canvas entity is opened for editing,
ComponentSourceManager::updateComponentInstances()runs automatically, upgrading all instances where safe. Modified trees are auto-saved, so the editor has the last word on publishing after previewing the changes.
Safe upgrade criteria (auto-upgraded)
- Adding or removing optional props → data loss considered acceptable #3568906-3: Handle upgrading and rendering not yet upgraded component instances of components with newly removed props or slots
- Adding or removing slots
- Changing requiredness (optional ↔ required)
- Changing the widget
- Changing default values or slot examples
- Adding required props that have an example value (the example is injected as default)
- Increase of cardinality (more values allowed) 🙋 needs issue
- Decrease of cardinality (fewer values allowed) → data loss also considered acceptable, see first point
Unsafe changes (NOT auto-upgraded)
Both of these would cause data loss:
- Changing prop shapes (field type / storage settings) — existing data may be incompatible with the new field type: #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs
Unsafe changes — auto-upgraded for CODE COMPONENTS
⚠️ But for code components, we specifically want to allow both of those unsafe changes, because the reality is already that such changes are allowed to be made to the code component itself:

Therefore, we must introduce a JsComponent-specific instance updater that extends GeneratedFieldExplicitInputUxComponentInstanceUpdater and allows 2 additional update operations:
- Code-component-only: drop stored explicit inputs in old component instance for props whose shape has changed in active version #3587711: Introduce a JsComponentInstanceUpdater that allows prop shape changes and accepts data loss
→ both of these should inform the content author of the data loss in a toast, see #3574257-6: Provide a toast informing editors about which Canvas component trees have been auto-updated, point 2.
Specific scenarios handled
- Removed props/slots (#3568906: Handle upgrading and rendering not yet upgraded component instances of components with newly removed props or slots): orphaned data is cleaned up on next edit.
- New required props with example values (#3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value): example values are injected as defaults for backward-compatible rendering and upgrading.
- Field type enforcement (#3560005: Require component instances to use the field type + storage + instance settings dictated by the `Component` version): component instances must use the field type + storage + instance settings dictated by their
Componentversion. - Corrupt StaticPropSource detection (#3558719: Linking then unlinking a field renders a wrong Field Widget: corrupt `StaticPropSource` is sent by client, server should detect this): server detects and rejects corrupt prop sources sent by the client.
- Garbage cleanup (#3524401: `GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput()` allows garbage to pile up):
validateComponentInput()no longer allows undefined props to pile up. - Components in active development (#3532514: Gracefully handle components in active development: ensure great DX): graceful handling with improved error visibility and DX.
- Cache-tags-aware prop shape repository (#3547579: Introduce a new cache tags aware prop shape repository, so changes affecting prop shape calculation can force the re-invoke of hook_canvas_storable_shape_prop_alter): ensures changes affecting prop shape calculation propagate correctly and trigger re-invocation of
hook_canvas_storable_shape_prop_alter. - Bundle-specific expression branches (#3550750: Adding bundle-specific expression branching support to `ReferenceFieldTypePropExpression`):
ReferenceFieldTypePropExpressionnow supports per-bundle expression branches for polymorphic reference fields. - Adding new multi-valued optional prop (#3579365: Component triggers assert when adding array prop to an in use component)
User interface changes
Original report
Discovered while discussing #3461499-17: Support complex SDC prop shapes: introduce (Storable)PropShape to compute field type storage settings in-depth with @lauriii.
Hypothetical scenario:
- A component has a prop with the schema
{type: string, format: date}. - This component has that prop populated by a
StaticPropSourcethat uses core'sdatetimefield type, with thedatetime_typesetting set todate. - The Site Builder decides that actually, this should be handled by a different field type: the
timestampfield type, plus theunix_to_dateadapter (which already exists:\Drupal\experience_builder\Plugin\Adapter\UnixTimestampToDateAdapter).
Existing content will continue to work, because the component instance is coupled to a StaticPropSource is self-containedComponent version (since #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions ⇒ less data to store per XB field row). It will continue to use the datetime field type.
But this also means the UX will be different: a different widget is presented.
So the Content Creator should have the ability to update the component's existing stored props to whichever the updated default field types are for each prop shape.
⚠️ For now this is pretty hypothetical: moving from one field type to another is not very likely because #3464003: [PP-1] [later phase] [needs design] Introduce "adapters" UX. Changing the widget is far more likely.
| Comment | File | Size | Author |
|---|---|---|---|
| Screenshot 2024-07-25 at 2.40.07 PM.png | 575.36 KB | wim leers |
Comments
Comment #3
wim leersComment #4
wim leersComment #5
wim leersJust realized that this is actually far out because of #3464003: [PP-1] [later phase] [needs design] Introduce "adapters" UX.
Comment #6
wim leersComment #7
wim leersFYI over at #3515629-16: FieldWidget's XB transforms must be bubbled by the Field Widget rendering to inform the client.2, I sketched how I now think this could actually happen automatically.
Comment #8
wim leersComing up again in #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions ⇒ less data to store per XB field row.
Comment #9
wim leersComment #10
wim leersComment #11
wim leersClosely related: #3528284: Add e2e tests that prove we can edit an old version of a component. That will lay the foundations for the necessary test coverage for this issue.
Comment #12
wim leersI think #5 is actually wrong — this is not blocked on adapters. Because at least a common subset is in principle possible to realize already.
Comment #14
wim leersA crucial piece of metadata was missing that would've prevented building this UX: tracking of which props are required #3532514: Gracefully handle components in active development: ensure great DX's first MR (!205) fixed that. 👍
That means this issue is now able to:
Which should be enough to build a first iteration
Comment #15
wim leersRelated: #3514672: [Needs design] DX & authoring experience: support `default` in addition to `examples[0]`, enables updating a component input across all existing instances.
Comment #16
wim leersComment #17
lauriiiComment #18
mglamanI think this is another scenario: could be triggered by an image code component and someone creating another media type with an image media source.
Comment #19
larowlanI think perhaps this could/should be a child of #3509115: [META] Plan to allow editing props and slots for in-use code components as it is dealing with the practical implications.
e.g with #3556340: Support deleting a prop from an in-use code component and #3524401: `GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput()` allows garbage to pile up we can't actually do GC because if the prop exists in the older version and that version is in use, we have to retain the values. But upgrading to the latest version allows us to do that GC
Thoughts?
Comment #20
lauriiiI'm second guessing myself from before by wondering if we should just automatically update all instances when the page is being edited. I'm not sure what the benefit of the 'Update to latest version' would be given that we would still render the component using the latest version. The only reason this could make sense is that if fields are removed, you could at least copy the values and move them somewhere. However, given that you wouldn't know what's being changed, it doesn't seem all that useful. If we wanted to solve for that, maybe we need a way to access stale data that was attached to a component before? It seems like that could be an incremental addition we would add at a later point.
Comment #21
wim leersFYI: #3558719: Linking then unlinking a field renders a wrong Field Widget: corrupt `StaticPropSource` is sent by client, server should detect this is about to land, and I have +1s for the direction I'm taking #3560005: Require component instances to use the field type + storage + instance settings dictated by the `Component` version in, which will make this MR way simpler and more feasible.
Which also means that @lauriii's #20:
… will actually become feasible for many things! 🚀
In a nutshell, we would be able to auto-update the
component_versionfor a component instance that has:inputschanges)inputsupon re-saving)hook_storage_prop_shape_alter()): automatic update possible (zeroinputschanges)👆 IMHO those are the scope of this issue. They result in old content entity revisions remaining unchanged, new content entity revisions using "the latest and greatest".
But … even for BC break scenarios, update paths become possible, but do inevitably require tailored update paths (i.e. code).
hook_storage_prop_shape_alter()): automatic update possible IF the developer provided an update path.👉
A) load all
Components that contain >=1 props of the affected prop shape (will become easier after #3547579: Introduce a new cache tags aware prop shape repository, so changes affecting prop shape calculation can force the re-invoke of hook_canvas_storable_shape_prop_alter)B) iterate over all versions (
Component::getVersions())C) find all instances using that version (
ComponentAudit)D) per component instance, perform whichever logic is necessary — e.g.
linktourifield type is a simple transformation, butimageto (media)entity_referenceis harder: requires an intermediaryMediaentity to be created for the previously referenceFileentity.E) set the
component_versionto the new ("active") versionF) either save as a new content entity revision (preferable, would keep past data/revisions, and they would all still work!) or as an updated config entity (which do not support revisions — so the following component instance-containing config entity types would be updated in-place:
ContentTemplate,PageRegionandPattern)NOTE: this does NOT need to happen in a single request. It could (and must) be done in interruptible batches. State tracking (to immediately continue where we left) would accelerate such an update path, but it'd always be possible to reconstruct the remaining work, too (thanks to
ComponentAudit).See the preparations for this from June 2025 in
\Drupal\Tests\canvas\Kernel\Plugin\Canvas\ComponentSource\ComponentInputsEvolutionTest::testStorablePropShapeChanges().👉
A) load the
ComponentB) iterate over all versions (
Component::getVersions())C) find all instances using that version (
ComponentAudit)D) per component instance, perform whichever logic is necessary — e.g. rename a key in the component instance's
inputto the new prop name, or populate a new required prop with a default value.
E) set the
component_versionto the new ("active") versionE) either save as a new content entity revision (likely preferable, would keep past data/revisions, but none of them would work, because the live component implementation requires data) or not — it's up to the SDC developer to decide
Comment #22
effulgentsia commentedAdding what this is postponed on as a related issue.
Comment #23
penyaskito#3560005: Require component instances to use the field type + storage + instance settings dictated by the `Component` version landed, so this is unpostponed now apparently!
Comment #24
mglamanThe proposed resolution needs to be revisited after #20 and #21.
@lauriii specifies we should auto-update, but in #21 only three cases of auto-updates are identified. What do for components which fail those qualifications? Especially with code components.
Comment #25
lauriiiLimiting the automatic updating to the three scenarios from #21 is not sufficient. The scenarios that require developer-provided update paths would create a significant burden for component authors. It shouldn't be on them to provide migration paths for changes to props.
We need to be able to handle all scenarios for prop and slot modifications. This is already the case for content modeling outside of Canvas components in Drupal, including Blocks. When you change field configuration for Blocks and other fieldable entities, it's reflected everywhere that entity is used without requiring manual upgrade path. We should match that experience.
Comment #26
effulgentsia commentedI think we should break this up into manageable steps. I wrote up #3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data as the first step. I'll write up the additional steps in the coming days.
Comment #27
wim leersRelated: #3567586-4: Not possible to change theme due to SDC dependencies.
#26++, thanks @effulgentsia!
Comment #28
wim leersGuidance posted at #3567413-2: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data and -3. Hope it helps.
Comment #29
effulgentsia commentedAfter #3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data, the next two steps (that can be done in parallel with each other) are:
Comment #30
penyaskitoCreated another follow-up: #3571362: [PP-1] When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions to the most recent possible version where there's no impact on existing data
Comment #31
penyaskitoCreated #3571366: Allow Canvas Patterns+PageRegions+ContentTemplates provided by Recipes to NOT specify component versions
Comment #32
penyaskitoComment #33
wim leers#3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data and #3568906: Handle upgrading and rendering not yet upgraded component instances of components with newly removed props or slots are in. →
PP-1The latter hit a Product vs Engineering mismatch post-merge though: #3568906-29: Handle upgrading and rendering not yet upgraded component instances of components with newly removed props or slots — based on discussion between @lauriii and @penyaskito that may spawn a new child issue.
@penyaskito: this probably should also be postponed on #3574026: Component instances are not updating in the same way in regions than other entities, right? If so, this is still
PP-2.Comment #34
penyaskitoCorrect, but I hope that after we fix the rendering part of #3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value, #3574026: Component instances are not updating in the same way in regions than other entities should be a "won't fix".
Comment #35
wim leers#3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value is in (only reopened for comment clarifications)!
Last one: #3574026: Component instances are not updating in the same way in regions than other entities.
Once that's done: what work remains on this task? → tagged
Comment #36
nagwani commentedComment #37
wim leersJust noting that once this is done, we should determine next steps on #3557271: [PP-2] BE: Support changing the data-type of a code component prop.
EDIT: link fixed.
Comment #38
wim leersReflecting the reality that all the actual work has been happening in child issues thanks to @effulgentsia's #29.
Comment #39
wim leersActually, should we close this? Is this now just a duplicate of #3547808: label_display block configuration value should not be translatable?
Comment #40
penyaskitoUpdating issue summary based on what was implemented already.
Still missing: what to do if the prop shape changes. I think I filled an issue for that, but cannot find it right now. Edit: #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs should work for that.
Leaving this open to create that, and decide what to do with all the child issues still open.
Comment #41
penyaskitoI think #39 meant #3509115: [META] Plan to allow editing props and slots for in-use code components.
This META is for the infra needed to be able to update component versions on existing trees.
#3509115 is about using this to allow the UI to edit Code Components. There's a close relationship between the two, but I wouldn't say they are duplicates.
The issue I was missing in #40 should be #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs, added as related now.
Comment #42
penyaskitoComment #43
penyaskitoMoving screenshot and shape changes proposal to #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs
Comment #44
penyaskitoAdded #3579365: Component triggers assert when adding array prop to an in use component for multivalued optional prop.
Comment #45
wim leers#3463996: [META] When the field type, storage/instance settings, widget, expression or requiredness for an SDC/code component prop changes, the Content Creator must be able to upgrade has happened and means that "multiple-cardinality props" aka "multi-value props" aka "
type: arrayprops" has surfaced a number of edge cases. For example #3587711: Introduce a JsComponentInstanceUpdater that allows prop shape changes and accepts data loss.This made me realize that actually #3577946: Remove Canvas Dev Mode flag for Multi-Value Props support should not have happened until some things here had been tackled:
JsComponentInstanceUpdater extends GeneratedFieldExplicitInputUxComponentInstanceUpdaterthat discards old values) — see #3587711-14: Introduce a JsComponentInstanceUpdater that allows prop shape changes and accepts data loss (and that have been updated before #3577946: Remove Canvas Dev Mode flag for Multi-Value Props support landed)And that in turn made me realize that another thing was missed in this meta, unrelated to #3577946's multiple-cardinality work:
JsComponentInstanceUpdater extends GeneratedFieldExplicitInputUxComponentInstanceUpdaterthat discards old values) — see #3587711-14: Introduce a JsComponentInstanceUpdater that allows prop shape changes and accepts data loss(Added all of those to the issue summary. LMK what you think, @penyaskito.)
P.S.: Apparently I'm the one who marked this "PP-1" almost 2 years ago in #5. That's no longer relevant.
Comment #46
penyaskito#45: I was scared for a second. We are NOT auto-upgrading if cardinality changes at the moment.
Edited IS to point that Increase of cardinality (more values allowed) needs an issue too.
Comment #47
wim leersYes, sorry, my bad — indeed all 4 of those still need to be implemented!
Other than that, does #45 make sense?
Comment #48
penyaskito#45 makes sense. We could even go further and when "decrease of cardinality" happens, we could check at the instance level if that instance still fits. e.g. if cardinality was 5, I'm reducing to 3, and a given instance has 3 values, it could be allowed.
Could be confusing UX-wise, but not worse than adding an instance to a tree that has instances than cannot be auto-updated.
Comment #49
wim leers#48: yep, that's definitely what I intended: only drop the "excess" values. We should minimize data loss for sure.
Clarified in IS.
Comment #50
wim leersRepurposed #3587711 to solve one of the 4 todos listed in #45.
Comment #51
wim leersAFAICT #3574257: Provide a toast informing editors about which Canvas component trees have been auto-updated's blockers have landed. It would really help in informing the user of the unsafe changes allowed for code components.
Also, child issue #3574257: Provide a toast informing editors about which Canvas component trees have been auto-updated was not listed as a user interface change yet, so added that.
Comment #52
wim leersQuoting @lauriii:
Wow is indeed considered safe, I forgot about that! @lauriii decided that Jan 29 apparently, in
#3568906-3: Handle upgrading and rendering not yet upgraded component instances of components with newly removed props or slots.
That simplifies things.