Back to Elementor

Prop Type Migrations

migrations/README.md

4.1.011.4 KB
Original Source

Prop Type Migrations

System Overview

This is a prop type migration system, not a general data migration system.

Trigger: Migrations run when it finds a mismatch between data prop type, and actual schema (code) (e.g., stringstring-v2). Without a type change, there's no trigger and no migration.

Scope: Having mismatch of type (e.g., stringstring-v2) will find all instances of mismatch, and send the object to run migration script on.

See Migration Scope for details.

Appendix

Structure

Manifest

Manifest describes the different migrations, it can contain widget key migrations and prop-type migrations

json
{
  "widgetKeys": {
    "e-logo": [
        { "from": "svg", "to": "icon" }
      ]
  },
  "propTypes": {
    "string-to-html": {
      "fromType": "string",
      "toType": "html",
      "url": "string-to-html.json"
    }
  }
}

Prop Type Migrations

Prop type migrations are a set of operations, up for upgrade and reverse down for downgrade.
Prop type migrations support wildcard paths and conditions (see below)

json
{
  "up": [
    {
      "op": {
        "fn": "set",
        "path": "$$type",
        "value": "html"
      }
    }
  ],
  "down": [
    {
      "op": {
        "fn": "set",
        "path": "$$type",
        "value": "string"
      }
    }
  ]
}

Paths

Path parameter works with wildcard, starting from the root of the prop object.

Important: For prop type migrations (the primary use case), paths always start at the prop root—not the widget or document root. For example:

  • $$type refers to the type field at the prop root
  • value.nested refers to propObject.value.nested
  • Wildcards like value.* or value.items[*] match children within the prop

Widget key migrations (rare) start at the widget/element root instead. See Migration Scope for details.

Conditions

Conditions check whether to run the migration or not, with many helper functions such as exists, conditions can be compounded by AND and OR.
Full list can be found here

Important Notes

Data Transformations

Migrations do NOT support value transformations. Migrations are purely structural - they create or update, but can't activate any functions on the data.

Handle transformations in transformer code

  • Migrations change structure: { "color": "#fff" }{ "color": { "$$type": "color", "value": "#fff" } }
  • Transformers can take current values, and transform them { "oldColor": "#fff", "newColor": {} } -> { "oldColor": "#fff", "newColor": { "gradient": "something", "value": "#fff" }}

Migration Scope

  • Prop Type Migrations: Operate on a single prop instance, paths start at prop root
  • Widget Key Migrations: Operate on entire widget element, paths start at element root
  • Migrations run before validation and transformation in the data processing pipeline

Example widget:

json
{
  "id": "heading-1",
  "elType": "widget",
  "widgetType": "e-heading",
  "settings": {
    "title": {
      "$$type": "string",
      "value": "Hello"
    },
    "tag": {
      "$$type": "string",
      "value": "h2"
    }
  }
}

Prop Type Migration (e.g., title migrating from stringstring-v2):

  • Operates on just the prop object:
    json
    {
      "$$type": "string",
      "value": "Hello"
    }
    
  • Paths are relative to prop root: $$type, value

Widget Key Migration (e.g., renaming taghtmlTag):

  • Operates on the entire widget/element
  • Paths are relative to widget settings root: tag, title.value

Performance Considerations

  • Migration state is cached per document with version + manifest hash
  • Cache clears on Elementor version change, manifest change, or feature flag toggle

Functions

Set

set creates or updates data, it can update key / value or both. Full Documentation here
Params:

  • key (optional)
  • value (optional)
  • merge default true - attempts to deep merge objects instead of replace

Usage

Replaces nested key and value

json
{ "op": { "fn": "set", "path": "value.*.nested", "value": ["a"], "key": "nested2" } }

Appends to array

json
{ "op": { "fn": "set", "path": "value.*.nested.[]", "value": "a" } }

Creates empty object at path

json
{ "op": { "fn": "set", "path": "value.*.nested.[*]" } }

Delete

delete removes keys/values at specified path. Full Documentation here
Params:

  • clean default true - deletes empty parent paths until reaching an object that has siblings

Usage

Delete specific nested key

json
{ "op": { "fn": "delete", "path": "value.deprecated" } }

Delete all matching wildcard paths

json
{ "op": { "fn": "delete", "path": "value.items[*].legacy" } }

Delete without cleaning empty parents

json
{ "op": { "fn": "delete", "path": "value.old", "clean": false } }

Move

move relocates values from one path to another. Full Documentation here
Params:

  • src - Source path
  • dest - Destination path
  • clean default true - deletes source path after move (will delete empty paths until reaching an object that has siblings)

Usage

Move simple value to new location

json
{ "op": { "fn": "move", "src": "value.oldField", "dest": "value.nested.newField" } }

Move without cleaning source

json
{ "op": { "fn": "move", "src": "value.data", "dest": "value.backup", "clean": false } }

Move nested object structure

json
{ "op": { "fn": "move", "src": "value.settings", "dest": "value.config.settings" } }

Examples

Widget Key Migration: Rename Settings Key

Context: Rename a widget settings key from taghtmlTag. Paths start at widget settings root.

json
//manifest.json
{
  "widgetKeys": {
    "e-heading": [
      { "from": "tag", "to": "htmlTag" }
  ],
  "propTypes": {}
}

Before:

json
{
  "id": "heading-1",
  "elType": "widget",
  "widgetType": "e-heading",
  "settings": {
    "tag": {
      "$$type": "string",
      "value": "h2"
    },
    "title": {
      "$$type": "string",
      "value": "Hello"
    }
  }
}

After:

json
{
  "id": "heading-1",
  "elType": "widget",
  "widgetType": "e-heading",
  "settings": {
    "htmlTag": {
      "$$type": "string",
      "value": "h2"
    },
    "title": {
      "$$type": "string",
      "value": "Hello"
    }
  }
}

Prop Type Migration: Change Type

Context: Migrate stringhtml type. Paths start at prop root.

json
{
  "up": [
    { "op": { "fn": "set", "path": "$$type", "value": "html" } }
  ],
  "down": [
    { "op": { "fn": "set", "path": "$$type", "value": "string" } }
  ]
}

Before: { "$$type": "string", "value": "Hello" }
After: { "$$type": "html", "value": "Hello" }

Prop Type Migration: Rename Keys (Simple and Wildcard)

Context: Rename keys using key parameter, with and without wildcards. Paths start at prop root.

Simple key rename:

json
{
  "up": [
    { "op": { "fn": "set", "path": "value.oldName", "key": "newName" } }
  ],
  "down": [
    { "op": { "fn": "set", "path": "value.newName", "key": "oldName" } }
  ]
}

Wildcard key rename across multiple objects:

json
{
  "up": [
    {
      "op": { "fn": "set", "path": "value.*.oldName", "key": "newName" },
      "condition": { "fn": "exists", "path": "value.*.oldName" }
    }
  ],
  "down": [
    {
      "op": { "fn": "set", "path": "value.*.newName", "key": "oldName" },
      "condition": { "fn": "exists", "path": "value.*.newName" }
    }
  ]
}

Before (wildcard example):

json
{
  "$$type": "responsive",
  "value": {
    "desktop": { "oldName": "value1" },
    "tablet": { "oldName": "value2" },
    "mobile": { "oldName": "value3" }
  }
}

After:

json
{
  "$$type": "responsive",
  "value": {
    "desktop": { "newName": "value1" },
    "tablet": { "newName": "value2" },
    "mobile": { "newName": "value3" }
  }
}

Prop Type Migration: Wildcards with Array Items

Context: Update all color types in a gradient using array wildcards [*]. Paths start at prop root.

json
{
  "up": [
    {
      "op": { "fn": "set", "path": "value.stops[*].color.$$type", "value": "color" },
      "condition": { "fn": "equals", "path": "value.stops[*].color.$$type", "value": "string" }
    }
  ],
  "down": [
    {
      "op": { "fn": "set", "path": "value.stops[*].color.$$type", "value": "string" },
      "condition": { "fn": "equals", "path": "value.stops[*].color.$$type", "value": "color" }
    }
  ]
}

Before:

json
{
  "$$type": "gradient",
  "value": {
    "stops": [
      { "position": 0, "color": { "$$type": "string", "value": "#ff0000" } },
      { "position": 50, "color": { "$$type": "string", "value": "#00ff00" } },
      { "position": 100, "color": { "$$type": "string", "value": "#0000ff" } }
    ]
  }
}

After:

json
{
  "$$type": "gradient",
  "value": {
    "stops": [
      { "position": 0, "color": { "$$type": "color", "value": "#ff0000" } },
      { "position": 50, "color": { "$$type": "color", "value": "#00ff00" } },
      { "position": 100, "color": { "$$type": "color", "value": "#0000ff" } }
    ]
  }
}

Prop Type Migration: Compound Conditions (AND/OR)

Context: Use and/or conditions to selectively migrate items. Paths start at prop root.

json
{
  "up": [
    {
      "op": { "fn": "set", "path": "value.items[*].type", "value": "enhanced" },
      "condition": {
        "fn": "and",
        "conditions": [
          { "fn": "equals", "path": "value.items[*].type", "value": "legacy" },
          { "fn": "exists", "path": "value.items[*].data" }
        ]
      }
    },
    {
      "op": { "fn": "set", "path": "value.items[*].migrated", "value": true },
      "condition": {
        "fn": "or",
        "conditions": [
          { "fn": "equals", "path": "value.items[*].type", "value": "enhanced" },
          { "fn": "not_exists", "path": "value.items[*].migrated" }
        ]
      }
    }
  ],
  "down": [
    {
      "op": { "fn": "set", "path": "value.items[*].type", "value": "legacy" },
      "condition": { "fn": "equals", "path": "value.items[*].type", "value": "enhanced" }
    },
    {
      "op": { "fn": "delete", "path": "value.items[*].migrated" }
    }
  ]
}

Before:

json
{
  "$$type": "list",
  "value": {
    "items": [
      { "type": "legacy", "data": { "content": "Item 1" } },
      { "type": "legacy", "data": { "content": "Item 2" } },
      { "type": "new", "data": { "content": "Item 3" } }
    ]
  }
}

After:

json
{
  "$$type": "list",
  "value": {
    "items": [
      { "type": "enhanced", "data": { "content": "Item 1" } },
      { "type": "enhanced", "data": { "content": "Item 2" } },
      { "type": "new", "data": { "content": "Item 3" } }
    ]
  }
}