cd ../blog

Microsoft Ships a Per-Property Migration Changelog Inside Your App. Nobody Reads It.

There is a machine-readable record of every property Microsoft added, removed, and renamed on the modern controls. It is sitting in your `.msapp` right now.

</>

Microsoft Ships a Per-Property Migration Changelog Inside Your App. Nobody Reads It.

There is a machine-readable record of every property Microsoft added, removed, and renamed on the modern controls. It is sitting in your .msapp right now.


If you have used the modern controls for more than a year, you have hit this: an app that worked stops working, or a property you set silently stops applying, or Alignment is suddenly Align and your theme component breaks. You search the docs. The docs describe the control as it is today. There is no changelog.

There is a changelog. It is inside your app file.

Unpack any recent .msapp and open References/Templates.json. Alongside the classic control templates you will find a PcfTemplates array, and inside each entry a field called PcfConversions:

{
  "Name": "PowerApps_CoreControls_TextCanvas",
  "Version": "0.0.51",
  "PcfConversions": [
    {
      "From": "0.0.13",
      "To": "0.0.14",
      "Action": [
        { "Name": "Alignment",     "Type": "rename", "NewName": "Align" },
        { "Name": "As",            "Type": "remove" },
        { "Name": "Truncate",      "Type": "remove" },
        { "Name": "AutoHeight",    "Type": "add" },
        { "Name": "VerticalAlign", "Type": "add" }
      ]
    }
  ]
}

That is a migration table. From version, To version, and the exact property-level add / remove / rename operations the platform applies when it upgrades your control across that boundary.

In the app we examined, this table covers 10 modern controls, 77 version transitions, and 298 individual property changes. It is the closest thing that exists to an official history of the modern control set — and it is more precise than a changelog would be, because it is executable.

The renames, in full

These are the ones that break formulas and component contracts.

ControlVersionChange
TextCanvas0.0.13 → 0.0.14AlignmentAlign
TextCanvas0.0.17 → 0.0.18ItalicFontItalic, StrikethroughFontStrikethrough, UnderlineFontUnderline
CheckboxCanvas0.0.6 → 0.0.7ValueChecked
TabList1.0.13 → 1.0.14AccessibilityLabelAccessibleLabel

And the effective renames, done as a remove plus an add — which the platform does not link for you:

ControlVersionOldNew
DatePickerCanvas0.0.2 → 0.0.3ValueSelectedDate
DatePickerCanvas0.0.16 → 0.0.17StartYear, EndYearStartDate, EndDate
DropdownCanvas0.0.18 → 0.0.19BaseColorBasePaletteColor
Toggle1.0.13 → 1.0.14BaseColorBasePaletteColor
TextInputCanvas0.0.38 → 0.0.39DelayOutputTriggerOutput
TabList2.1.1 → 2.1.2RenderSizeSize
TabList1.0.10 → 1.0.11IsVerticalAlignment

ValueSelectedDate on the date picker is a remove and an add. There is no rename action linking them. If you were binding to Value, the migration deleted your binding.

The event properties that quietly disappeared

OnSelect was removed from five modern controls:

ControlVersionRemoved
TextCanvas0.0.14 → 0.0.15OnSelect, OnChange
ComboboxCanvas0.0.9 → 0.0.10OnSelect
DropdownCanvas0.0.12 → 0.0.13OnSelect
DatePickerCanvas0.0.2 → 0.0.3OnSelect
LinkCanvas0.0.4 → 0.0.5OnSelect

OnChange was removed from ButtonCanvas (0.0.13→14), Toggle (1.0.8→9), LinkCanvas (0.0.14→15) and CheckboxCanvas (0.0.12→13) — though the checkbox got a genuine upgrade in exchange, gaining OnSelect, OnCheck and OnUncheck in the same hop.

If you ever wondered why the modern text control has no OnSelect while the classic label does: it had one, for five versions, and then it did not.

The best part: the borders that shipped, got yanked, and came back

Read these three rows together.

TextCanvas   0.0.25 → 0.0.26   add     BorderStyle, BorderThickness, BorderColor (+ 4 radii, 4 paddings, Fill)
TextCanvas   0.0.36 → 0.0.37   remove  BorderStyle, BorderThickness, BorderColor
TextCanvas   0.0.37 → 0.0.38   add     BorderStyle, BorderThickness, BorderColor

Added at 26. Removed at 37. Re-added at 38 — one version later.

Now the same pattern across the family:

ControlRemoved atRe-added at
TextCanvas0.0.36 → 0.0.370.0.37 → 0.0.38
TextInputCanvas0.0.36 → 0.0.370.0.37 → 0.0.38
ComboboxCanvas0.0.37 → 0.0.380.0.38 → 0.0.39
LinkCanvas0.0.37 → 0.0.380.0.38 → 0.0.39
DropdownCanvas0.0.39 → 0.0.400.0.40 → 0.0.41
ButtonCanvas0.0.40 → 0.0.410.0.41 → 0.0.42
DatePickerCanvas0.0.30 → 0.0.310.0.31 → 0.0.32

Seven of the ten modern controls removed their border properties and restored them exactly one version later.

The removals also took HoverBorderColor, PressedBorderColor, DisabledBorderColor and FocusedBorderColor with them on five of those controls — and those four did not come back in the restore.

You are looking at a coordinated rollback and re-land, recorded in the format the platform uses to migrate your app. This is what a bad release looks like from the inside. If your modern controls lost their hover borders somewhere along the way and never got them back, this table is why.

Size on TextCanvas has the same shape at smaller scale: removed at 0.0.9 → 0.0.10, re-added at 0.0.10 → 0.0.11.

The theming build-out, reconstructed

The conversion table also tells you the order in which the modern controls became themeable — which explains why styling them has felt inconsistent for years.

  1. BasePaletteColor arrives first, control by control: ButtonCanvas 0.0.18, TabList 2.2.1, ComboboxCanvas 0.0.15, TextInputCanvas 0.0.16, DatePickerCanvas 0.0.7, CheckboxCanvas 0.0.14, LinkCanvas 0.0.14. One knob, one palette colour.
  2. FontSize next, as a property separate from Size, again staggered across seven controls.
  3. The full font blockFont, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough — lands as a single 6-property add: TextCanvas 0.0.18, ButtonCanvas 0.0.26, ComboboxCanvas 0.0.21, TextInputCanvas 0.0.22, DatePickerCanvas 0.0.13, CheckboxCanvas 0.0.20, Toggle 1.0.20, TabList 2.2.9.
  4. The appearance blockAppearance, Fill, borders, radii, four paddings, 14 properties at once — lands last: TextInputCanvas 0.0.27, ComboboxCanvas 0.0.28, DropdownCanvas 0.0.30, DatePickerCanvas 0.0.21, LinkCanvas 0.0.28.
  5. Then the rollback above.

Same properties, same groupings, seven different version numbers. There was a plan; it was rolled out per-control rather than atomically. Every “why does this control not have that property yet” question you have ever asked is a row in this table.

The size knobs came late and separately: CheckboxSize at CheckboxCanvas 0.0.23, ToggleSize at Toggle 1.0.33. Required arrived at ComboboxCanvas 0.0.44. ValidationState swept four controls in the 0.0.16–0.0.21 range.

The complete version overview, per control

Everything above is a selection. Here is the whole table — every recorded hop for all ten modern controls, generated directly from the file rather than transcribed.

Read + as added, as removed, as renamed. “Shipping” is the version this app was authored against; hops below it are the history the migration engine replays to bring an older control up to current.

TextCanvas — shipping 0.0.51, 8 recorded hops, 32 property changes

HopAddedRemovedRenamed
0.0.90.0.10Size
0.0.100.0.11Size
0.0.130.0.14AutoHeight, VerticalAlignAs, TruncateAlignment → Align
0.0.140.0.15OnSelect, OnChange
0.0.170.0.18Font, FontColorItalic → FontItalic, Strikethrough → FontStrikethrough, Underline → FontUnderline
0.0.250.0.26Fill, PaddingTop, PaddingBottom, PaddingLeft, PaddingRight, BorderThickness, BorderColor, BorderRadiusTopLeft, BorderRadiusTopRight, BorderRadiusBottomRight, BorderRadiusBottomLeft, BorderStyle
0.0.360.0.37BorderStyle, BorderThickness, BorderColor
0.0.370.0.38BorderStyle, BorderThickness, BorderColor

TextInputCanvas — shipping 0.0.54, 10 recorded hops, 39 property changes

HopAddedRemovedRenamed
0.0.130.0.14MaxLength, Placeholder, Type
0.0.150.0.16BasePaletteColor
0.0.160.0.17ValidationState
0.0.180.0.19FontSize
0.0.210.0.22Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.220.0.23Align
0.0.260.0.27Appearance, BorderColor, BorderRadius, BorderStyle, BorderThickness, DisabledBorderColor, Fill, FocusedBorderColor, HoverBorderColor, PaddingBottom, PaddingLeft, PaddingRight, PaddingTop, PressedBorderColor
0.0.360.0.37BorderStyle, BorderThickness, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, BorderColor
0.0.370.0.38BorderStyle, BorderThickness, BorderColor
0.0.380.0.39TriggerOutputDelayOutput

ButtonCanvas — shipping 0.0.45, 7 recorded hops, 40 property changes

HopAddedRemovedRenamed
0.0.130.0.14OnChange
0.0.170.0.18BasePaletteColor
0.0.200.0.21FontSize
0.0.250.0.26Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.290.0.30BorderStyle, BorderThickness, BorderColor, BorderRadiusTopLeft, BorderRadiusTopRight, BorderRadiusBottomRight, BorderRadiusBottomLeft, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft, Align, VerticalAlign, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, Icon, Layout, IconRotation, IconStyle
0.0.400.0.41BorderStyle, BorderThickness, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, BorderColor
0.0.410.0.42BorderStyle, BorderThickness, BorderColor

ComboboxCanvas — shipping 0.0.51, 11 recorded hops, 39 property changes

HopAddedRemovedRenamed
0.0.40.0.5IsSearchable, InputTextPlaceholder
0.0.60.0.7SearchText
0.0.90.0.10OnSelect
0.0.140.0.15BasePaletteColor, FontSize
0.0.160.0.17ValidationState
0.0.200.0.21Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.270.0.28Appearance, BorderColor, BorderRadius, BorderStyle, BorderThickness, DisabledBorderColor, Fill, FocusedBorderColor, HoverBorderColor, PaddingBottom, PaddingLeft, PaddingRight, PaddingTop, PressedBorderColor
0.0.370.0.38BorderStyle, BorderThickness, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, BorderColor
0.0.380.0.39BorderStyle, BorderThickness, BorderColor
0.0.430.0.44Required
0.0.460.0.47TriggerOutput
HopAddedRemovedRenamed
0.0.120.0.13OnSelect
0.0.170.0.18BaseColor, FontSize
0.0.180.0.19BasePaletteColorBaseColor
0.0.200.0.21ValidationState
0.0.290.0.30Appearance, BorderColor, BorderRadius, BorderStyle, BorderThickness, DisabledBorderColor, Fill, FocusedBorderColor, HoverBorderColor, PaddingBottom, PaddingLeft, PaddingRight, PaddingTop, PressedBorderColor
0.0.390.0.40BorderStyle, BorderThickness, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, BorderColor
0.0.400.0.41BorderStyle, BorderThickness, BorderColor

DatePickerCanvas — shipping 0.0.46, 9 recorded hops, 46 property changes

HopAddedRemovedRenamed
0.0.20.0.3SelectedDate, IsEditable, Format, StartYear, EndYear, StartOfWeek, PlaceholderValue, OnSelect
0.0.60.0.7BasePaletteColor
0.0.70.0.8ValidationState
0.0.90.0.10FontSize
0.0.120.0.13Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.160.0.17EndDate, StartDateEndYear, StartYear
0.0.200.0.21Appearance, BorderColor, BorderRadius, BorderStyle, BorderThickness, DisabledBorderColor, Fill, FocusedBorderColor, HoverBorderColor, PaddingBottom, PaddingLeft, PaddingRight, PaddingTop, PressedBorderColor
0.0.300.0.31BorderStyle, BorderThickness, HoverBorderColor, PressedBorderColor, DisabledBorderColor, FocusedBorderColor, BorderColor
0.0.310.0.32BorderStyle, BorderThickness, BorderColor

That first row is the most destructive single hop in the dataset. Seven properties added, Value and OnSelect both removed, in one step — and ValueSelectedDate is recorded as a remove plus an add, not a rename, so nothing links the old binding to the new one.

CheckboxCanvas — shipping 0.0.30, 6 recorded hops, 14 property changes

HopAddedRemovedRenamed
0.0.60.0.7Value → Checked
0.0.120.0.13OnSelect, OnCheck, OnUncheckOnChange
0.0.130.0.14BasePaletteColor
0.0.150.0.16FontSize
0.0.190.0.20Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.220.0.23CheckboxSize

LinkCanvas — shipping 0.0.45, 8 recorded hops, 33 property changes

HopAddedRemovedRenamed
0.0.40.0.5As, OnSelect
0.0.130.0.14BasePaletteColor
0.0.140.0.15Align, AutoHeight, VerticalAlign, WrapOnChange
0.0.160.0.17FontSize
0.0.210.0.22Font, FontWeight, FontItalic, FontUnderline, FontStrikethrough
0.0.270.0.28Appearance, Fill, BorderStyle, BorderThickness, BorderColor, BorderRadiusTopLeft, BorderRadiusTopRight, BorderRadiusBottomRight, BorderRadiusBottomLeft, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft
0.0.370.0.38BorderStyle, BorderThickness, BorderColor
0.0.380.0.39BorderStyle, BorderThickness, BorderColor

Toggle — shipping 1.1.5, 5 recorded hops, 12 property changes

HopAddedRemovedRenamed
1.0.81.0.9OnChange
1.0.101.0.11BaseColor, FontSize
1.0.131.0.14BasePaletteColorBaseColor
1.0.191.0.20Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough
1.0.321.0.33ToggleSize

TabList — shipping 2.2.31, 6 recorded hops, 13 property changes

HopAddedRemovedRenamed
1.0.101.0.11AlignmentIsVertical
1.0.131.0.14AccessibilityLabel → AccessibleLabel
2.1.12.1.2SizeRenderSize
2.2.02.2.1BasePaletteColor
2.2.22.2.3FontSize
2.2.82.2.9Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough

Reading the tables sideways

Three patterns only become visible once all ten are side by side.

The hops are sparse, and the gaps are the story. TextInputCanvas ships at 0.0.54 but its last recorded hop is 0.0.38 → 0.0.39. Sixteen versions with no entry at all. A hop exists only when the property surface changes, so those sixteen releases were bug fixes and rendering work. This is a changelog of contract changes, not of releases.

Nearly every control gained the same six font properties, at eight different times. Font, FontColor, FontWeight, FontItalic, FontUnderline, FontStrikethrough land as one block on seven controls — DatePickerCanvas 0.0.13, CheckboxCanvas 0.0.20, ComboboxCanvas 0.0.21, TextInputCanvas 0.0.22, ButtonCanvas 0.0.26, Toggle 1.0.20, TabList 2.2.9 — and on LinkCanvas 0.0.22 as five of the six, without FontColor.

TextCanvas is the exception, and an instructive one: at 0.0.18 it adds only Font and FontColor, then renames Italic, Strikethrough and Underline into FontItalic, FontStrikethrough, FontUnderline. It reaches the same six-property surface as the others by a different route, because it already had those properties under their old names. Gen 3 then renames all of them straight back.

The 14-property appearance block is equally staggered, landing at DatePickerCanvas 0.0.21, TextInputCanvas 0.0.27, ComboboxCanvas 0.0.28, DropdownCanvas 0.0.30 — then being partly torn out again roughly ten versions later on each.

Totals across all ten: 77 hops, 298 property changes — 229 adds, 63 removals, 6 renames.

Those six renames are the entire set the platform records for the modern controls:

ControlVersionRename
TextCanvas0.0.13 → 0.0.14AlignmentAlign
TextCanvas0.0.17 → 0.0.18ItalicFontItalic
TextCanvas0.0.17 → 0.0.18StrikethroughFontStrikethrough
TextCanvas0.0.17 → 0.0.18UnderlineFontUnderline
TabList1.0.13 → 1.0.14AccessibilityLabelAccessibleLabel
CheckboxCanvas0.0.6 → 0.0.7ValueChecked

Six renames against 63 removals is the ratio worth noticing. When Microsoft changes a property on these controls it is roughly ten times more likely to delete it than to rename it — and a rename is the only one of the two the migration engine can follow automatically.

Postscript: the boundary the table does not cover — and Microsoft’s own guide

Everything above describes movement within the PowerApps_CoreControls_* generation. In a later export (August 2026) we found a third generation: modernText, modernTextInput, modernCombobox, modernDatePicker, back in the classic shared-template format at ~300 bytes per instance instead of 71 KB.

PcfConversions says nothing about that boundary. There is no hop from PowerApps_CoreControls_TextCanvas to modernText. So we derived the mapping by diffing property surfaces — and Microsoft’s Recent updates to modern controls in canvas apps guide lets us check the work.

A note on sourcing, because it matters for what follows. Everything derived from template diffs below is reproducible from the commands in the Methodology section — run them yourself and you will get the same answer. Everything attributed to Microsoft’s guide is quoted from that documentation and has not been independently verified against a running environment. Where the two agree, that is a genuine cross-check. Where the guide is the only source, it is labelled as such.

The derived renames match the documentation exactly:

Derived from templatesMicrosoft’s guideMatch
FontColorColorFontColorColor
FontSizeSizeFontSizeSize
FontItalicItalicFontItalicItalic
FontStrikethroughStrikethroughsame
FontUnderlineUnderlinesame
WeightFontWeight (Text only)WeightFontWeight, “Text only”
BorderRadius → four corner propertiesRadiusTopLeft/TopRight/BottomLeft/BottomRight
TabIndex absent from gen 3”TabIndex removed” (Icon)
OnSelect present on modernText”new OnSelect event” (Text)

Every one of those was recoverable from the files before the guide existed. The entire Font* prefix is dropped — the same six-property block that gen 2 spent two years adding to eight controls, one at a time, is renamed away wholesale.

Two things the guide adds that no amount of template diffing would have revealed, and they are the dangerous ones:

1. String values became enums. Properties that took "Bold" or "Start" now require FontWeight.Bold, Align.Left, ValidationState.Error, Appearance.FilledDarker, BorderStyle.Solid, IconStyle.Outline. The property name is unchanged, so a name-level diff sees nothing — but a formula passing the old string breaks. Worse: FontWeight.Medium no longer exists and silently maps to FontWeight.Normal on update. Your semibold text quietly becomes regular.

2. OnChange fires at different times. Same property, same name, different semantics:

ControlWasNow
Text Inputevery keystrokefocus out only
Number Inputevery keystrokefocus out + step clicks
Combo Boxdelayed / focus outevery selection
Radioinconsistentevery selection, reliably

Microsoft flags the Text Input change as critical, and it is: any app doing live search or running validation on each keystroke stops working the moment the control updates — with no error, no warning, and no property rename to detect. The recommended fix is to reference the output property (TextInput1.Text) directly instead of relying on OnChange.

Neither category appears in PcfConversions, because the table records only add/remove/rename of property names. A value-domain change and a timing change are both invisible to it. That is the honest ceiling on what the embedded changelog can tell you.

The guide also confirms the scope is much larger than our sample: 16 controls have updated versions (Text, Number Input, Date Picker, Text Input, Tab List, Combo Box, Radio, Link, Info Button, Button, Dropdown, Icon, Slider, Toggle, Checkbox, Form). Our app only used four of them, and an .msapp only embeds what it uses.

Also worth noting for anyone reading the tables above: ValueDefault shows up again in gen 3, on Slider and (as DefaultSelectedItemsDefault) on Dropdown. On modernTextInput the old read/write Value is split into Default (settable) and Text (direction="out") — one property becoming two, with a direction change. No rename action can express that, which is exactly why it needs a guide and not a table.

Why this is worth your attention

It is a real API contract. PcfConversions is not documentation, it is the migration engine’s input. It is authoritative in a way the docs are not.

It is machine-readable. Roughly 120 lines of code turns it into a linter: parse your app’s control versions, walk forward through the conversion table, and flag every formula referencing a property that a future hop removes or renames. That is a pre-upgrade break report for canvas apps, and it does not exist today.

It is a version pin you did not know you had. Your app records the control version it was authored against. The gap between that and current is measured in these hops.

It is the honest history of the modern control set. 298 property changes over 77 version hops across 10 controls — every one a potential breakage that shipped without a release note.


Methodology

Sample: one production .msapp exported 2026-02-16. A 2023-era app we compared against has no PcfTemplates key at all — the modern control machinery did not exist in that format yet.

unzip -q app.msapp -d app/
python3 - <<'EOF'
import json
d = json.load(open('app/References/Templates.json'))
for t in d.get('PcfTemplates', []):
    print(f"## {t['Name']} @ {t['Version']}")
    for c in t['PcfConversions'] or []:
        acts = '; '.join(
            f"{a['Type']} {a['Name']}" + (f" -> {a['NewName']}" if a.get('NewName') else '')
            for a in c['Action'])
        print(f"  {c['From']} -> {c['To']}: {acts}")
EOF

The table only lists controls your app actually uses, so coverage varies per app. To see all ten you need an app that uses all ten — or you union the tables from several apps, which is what we did.