When to Use
Move compositions between stacks: uid, index and schema remaps, and what breaks if you skip them.
Part of the API-authoring set. Start at author-composition-via-api, which routes to this one.
Migrating Compositions Between Stacks: Uid, Index, and Schema Remaps
Copying compositions from a source stack (a dev/staging stack, another project) to a destination is NOT a byte copy. The entry JSON carries three classes of stack-bound values that MUST be rewritten, or the composition writes cleanly, resolves in the SSR fetch, and still renders blank or errors in the canvas. All three are verified live (from a non-prod data center to NA prod).
1. composable_uid must be RE-DERIVED to each destination entry’s NEW uid: not carried over
Studio’s invariant is composable_uid == entry uid. On the destination, POST gives each section a new entry uid, so its composable_uid must be updated to that new uid, and every reference to it rewritten. Preserve the source composable_uid and you hit a split failure that looks like it works:
- SSR fetchCompositionData resolves fine: it matches sections by composable_uid, which you preserved. sectionCompositions comes back fully populated. Everything looks correct server-side.
- The canvas + client renderer break: they key the section registry by entry uid, not composable_uid. The template’s section-composition nodes reference sections by metadata.compositionUID (= the source uid), which matches no destination entry uid, so you get Component with type '<uid>' is not registered, once per section, in the canvas and the client render.
The fix (do all three, in order):
- Create each section on the destination. Capture its new entry uid.
- PUT each section’s composable_uid = its own new entry uid.
- In the template: rewrite every ui section-composition node’s metadata.compositionUID from the source uid to the destination uid, and set linked_sections to the destination uids ({uid, _content_type_uid}, the compositions CT). Publish.
linked_sections is a reference field storing entry uids: those are always stack-specific and always need remapping (see § Authoring a Template: populate linked_sections). metadata.compositionUID inside the ui is what the client resolves by, so it must match the new uid too.
2. Block-index paths must be remapped when the destination CT’s field ORDER differs
Bindings into an ordered multi-value field bake the index into the path: { modular_blocks: { "2": { image_grid: {} } } }. If the destination entry orders those blocks differently (an extra block inserted, a different author order), every baked index points at the wrong block, which produces wrong data or a blank. Before writing, diff the source vs destination entry’s block order and rewrite each index in the ui binding paths (the path flattener keeps numeric keys, so a global old→new index map over modular_blocks.<N> is enough).
3. Binding paths + selected_field must match the DESTINATION CT’s schema, not the source’s
The same logical field can sit at a different path across stacks (schema drift): a hero image at image in one stack, image_options.image in another. A migrated binding of hero.0.image.url silently resolves to nothing on a stack whose hero_banner nests the file under image_options. Symptom: text/scalar props render (their paths matched) but one field is blank. Verify each bound path against the destination CT schema (GET /v3/content_types/<ct>), and fix the drifted ones. Same for linked_schemas.selected_field.
4. Assets are stack-scoped
File/image fields reference asset uids that don’t exist on the destination. Either re-upload the assets to the destination (raw multipart POST /v3/assets, then publish) and re-point the fields, or bind to existing destination assets. A migrated composition whose entry data still points at source-stack asset uids renders with broken/blank media.
Pre-flight for a cross-stack migration
Verify BOTH paths, exactly as § Authoring a Template prescribes: SSR resolves (fetch returns the sections) and the canvas opens without not registered errors. The uid remap (#1) is the one that passes SSR and fails the canvas, so an SSR-only check is not enough.