Ionic Framework Version
(Not present in v8.8.19.)
Current Behavior
ion-radio's value property is now reflected to the value attribute. Because an attribute is a string, any non-string value is round-tripped through the attribute and destroyed:
| assignment | v8.8.19 | v9.0.0 |
|---|
radio.value = false | prop=false, attr=null | prop=false, attr="ion-rb-0" |
radio.value = true | prop=true, attr=null | prop="", attr="" |
click the radio → group.value | true | "" |
A boolean true reflects as the empty attribute value="" (boolean attribute semantics), and the attribute-changed callback then parses it back into the property as "". The radio still renders as visually checked, so the UI looks correct while ion-radio-group's value — and therefore any framework binding — is silently wrong.
This also conflicts with the documented compareWith property, which exists specifically so that ion-radio values can be objects.
Expected Behavior
value should keep whatever value it was assigned, as in v8. Reflecting an any-typed property to an attribute is lossy and should not happen.
Steps to Reproduce
ionic start radioValue blank --type=vue (framework is irrelevant — this reproduces on bare web components).- Install
@ionic/core@9.0.0. - Render:
<ion-radio-groupid="group"><ion-radioid="r-false">Public</ion-radio><ion-radioid="r-true">Restricted</ion-radio></ion-radio-group>
- After the components hydrate, run:
document.getElementById('r-false').value=falsedocument.getElementById('r-true').value=true// v8: true / null v9: "" / ""console.log(document.getElementById('r-true').value,document.getElementById('r-true').getAttribute('value'))- Click the second radio and read
document.getElementById('group').value.
Actual (v9):""
Expected:true
Code Reproduction URL
No Stackblitz link, sorry — but the reproduction above needs no framework at all: two <ion-radio> elements, two property assignments and a console.log. It reproduces on bare @ionic/core and flips purely on the version. Happy to put it on Stackblitz if that would help triage.
Ionic Info
Ionic:
Ionic CLI : 7.2.1
Ionic Framework : @ionic/vue 9.0.0
Capacitor:
Capacitor CLI : 8.5.0
@capacitor/android : 8.5.0
@capacitor/core : 8.5.0
@capacitor/ios : 8.5.0
System:
NodeJS : v26.7.0
npm : 11.19.0
OS : macOS
Additional Information
Cause
The Stencil member flags for ion-radio's value changed between versions (@ionic/core/components, minified proxy metadata):
-"ion-radio",{color:[513],name:[1],disabled:[4],value:[8],labelPlacement:[1,"label-placement"],…}+"ion-radio",{color:[513],name:[1],disabled:[4],value:[520],labelPlacement:[1,"label-placement"],…}8 is MEMBER_FLAGS.Any; 520 is 512 | 8, i.e. ReflectAttr | Any. So @Prop() on value gained reflect: true.
The interaction with connectedCallback makes the false case odd too:
connectedCallback(){if(this.value===undefined){this.value=this.inputId}…}which is why a falsy value leaves the generated ion-rb-N id in the reflected attribute.
I scanned every component's proxy metadata across 8.8.19 → 9.0.0: ion-radio.value is the only prop that gained the reflect bit, so reverting it should be low-risk.
Suggested fix
Drop reflect: true from ion-radio's value prop (restore value:[8]). We worked around it by giving our radios string values, which is arguably the more idiomatic choice anyway — but the reflect is still lossy for anyone relying on boolean, number or compareWith object values.
Impact
Any ion-radio with a non-string value breaks silently — the radio appears selected while the bound model receives "". In our app this hid a whole conditional section (v-if on a boolean bound to the radio group) and failed 3 E2E tests, with a misleading symptom: the element that should have appeared "was never found".
Found while upgrading one app from 8.8.19 to 9.0.0; two more regressions from the same release: #31392 and #31393, both in @ionic/vue's class syncing.
Ionic Framework Version
(Not present in v8.8.19.)
Current Behavior
ion-radio'svalueproperty is now reflected to thevalueattribute. Because an attribute is a string, any non-string value is round-tripped through the attribute and destroyed:radio.value = falseprop=false,attr=nullprop=false,attr="ion-rb-0"radio.value = trueprop=true,attr=nullprop="",attr=""group.valuetrue""A boolean
truereflects as the empty attributevalue=""(boolean attribute semantics), and the attribute-changed callback then parses it back into the property as"". The radio still renders as visually checked, so the UI looks correct whileion-radio-group's value — and therefore any framework binding — is silently wrong.This also conflicts with the documented
compareWithproperty, which exists specifically so thation-radiovalues can be objects.Expected Behavior
valueshould keep whatever value it was assigned, as in v8. Reflecting anany-typed property to an attribute is lossy and should not happen.Steps to Reproduce
ionic start radioValue blank --type=vue(framework is irrelevant — this reproduces on bare web components).@ionic/core@9.0.0.document.getElementById('group').value.Actual (v9):
""Expected:
trueCode Reproduction URL
No Stackblitz link, sorry — but the reproduction above needs no framework at all: two
<ion-radio>elements, two property assignments and aconsole.log. It reproduces on bare@ionic/coreand flips purely on the version. Happy to put it on Stackblitz if that would help triage.Ionic Info
Additional Information
Cause
The Stencil member flags for
ion-radio'svaluechanged between versions (@ionic/core/components, minified proxy metadata):8isMEMBER_FLAGS.Any;520is512 | 8, i.e.ReflectAttr | Any. So@Prop()onvaluegainedreflect: true.The interaction with
connectedCallbackmakes thefalsecase odd too:which is why a falsy value leaves the generated
ion-rb-Nid in the reflected attribute.I scanned every component's proxy metadata across 8.8.19 → 9.0.0:
ion-radio.valueis the only prop that gained the reflect bit, so reverting it should be low-risk.Suggested fix
Drop
reflect: truefromion-radio'svalueprop (restorevalue:[8]). We worked around it by giving our radios string values, which is arguably the more idiomatic choice anyway — but the reflect is still lossy for anyone relying on boolean, number orcompareWithobject values.Impact
Any
ion-radiowith a non-string value breaks silently — the radio appears selected while the bound model receives"". In our app this hid a whole conditional section (v-ifon a boolean bound to the radio group) and failed 3 E2E tests, with a misleading symptom: the element that should have appeared "was never found".Found while upgrading one app from 8.8.19 to 9.0.0; two more regressions from the same release: #31392 and #31393, both in
@ionic/vue's class syncing.