Ionic Framework Version
(Not present in v8.8.19.)
Current Behavior
In @ionic/vue 9, writing a class attribute across several lines on an Ionic component throws and tears down the render:
InvalidCharacterError: Failed to execute 'add' on 'DOMTokenList':
The token provided ('my-class\n') contains HTML space characters, which are not valid in tokens.
Follow-on errors from the broken render are typically RangeError: Maximum call stack size exceeded and TypeError: Cannot read properties of null (reading 'emitsOptions'), which make the original cause easy to miss.
Multi-line class attributes are valid HTML and valid Vue, are unremarkable in a formatted template, and worked in v8.
Expected Behavior
The class string should be tokenised on any ASCII whitespace, as the HTML parser and DOMTokenList do. The component should render normally.
Steps to Reproduce
ionic start multilineClass blank --type=vue- Install
@ionic/vue@9.0.0 / @ionic/vue-router@9.0.0 / @ionic/core@9.0.0. - Put this in a page:
<template>
<ion-contentclass=" first-class second-class"
>
content
</ion-content>
</template>
<script setup>import { IonContent } from'@ionic/vue'</script>- Load the page and open the console.
Actual:InvalidCharacterError is thrown and the page fails to render.
Expected: the element gets both classes and renders.
The same template on @ionic/vue@8.8.19 works.
Note this only reproduces through Vue's class binding, not through plain HTML — the HTML parser normalises the attribute before Ionic sees it, so a static .html page with the same markup is fine.
Code Reproduction URL
No Stackblitz link, sorry — but the reproduction is fully inlined in Steps to Reproduce above and needs nothing beyond a stock ionic start … --type=vue app with that snippet pasted into a single page. The behaviour also flips purely on the @ionic/vue version, with no other change. 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
getComponentClasses splits on a literal single space (@ionic/vue/dist/index.js):
constgetComponentClasses=(classes)=>{returnclasses?.split(' ')||[];};A multi-line attribute value therefore yields tokens such as "first-class\n", which syncElementClasses — new in v9 — passes straight to classList.add():
componentClasses.forEach((c)=>{if(!!c&&!element.classList.contains(c)){element.classList.add(c);// throws}});The !!c guard drops empty tokens but not whitespace-bearing ones. In v8 the split result was never handed to classList.add() — it only fed a filter — so malformed tokens were harmless.
Suggested fix
constgetComponentClasses=(classes)=>{returnclasses?.split(/\s+/).filter(Boolean)||[];};We fixed this in our own templates by keeping class attributes on one line, and additionally carry the split fix in a local pnpm patch as insurance.
Found while upgrading one app from 8.8.19 to 9.0.0; two more regressions from the same release: #31392 (cumulative Set in syncElementClasses resurrects removed conditional classes) and #31394 (ion-radio value reflect).
Ionic Framework Version
(Not present in v8.8.19.)
Current Behavior
In
@ionic/vue9, writing aclassattribute across several lines on an Ionic component throws and tears down the render:Follow-on errors from the broken render are typically
RangeError: Maximum call stack size exceededandTypeError: Cannot read properties of null (reading 'emitsOptions'), which make the original cause easy to miss.Multi-line class attributes are valid HTML and valid Vue, are unremarkable in a formatted template, and worked in v8.
Expected Behavior
The class string should be tokenised on any ASCII whitespace, as the HTML parser and
DOMTokenListdo. The component should render normally.Steps to Reproduce
ionic start multilineClass blank --type=vue@ionic/vue@9.0.0/@ionic/vue-router@9.0.0/@ionic/core@9.0.0.Actual:
InvalidCharacterErroris thrown and the page fails to render.Expected: the element gets both classes and renders.
The same template on
@ionic/vue@8.8.19works.Note this only reproduces through Vue's
classbinding, not through plain HTML — the HTML parser normalises the attribute before Ionic sees it, so a static.htmlpage with the same markup is fine.Code Reproduction URL
No Stackblitz link, sorry — but the reproduction is fully inlined in Steps to Reproduce above and needs nothing beyond a stock
ionic start … --type=vueapp with that snippet pasted into a single page. The behaviour also flips purely on the@ionic/vueversion, with no other change. Happy to put it on Stackblitz if that would help triage.Ionic Info
Additional Information
Cause
getComponentClassessplits on a literal single space (@ionic/vue/dist/index.js):A multi-line attribute value therefore yields tokens such as
"first-class\n", whichsyncElementClasses— new in v9 — passes straight toclassList.add():The
!!cguard drops empty tokens but not whitespace-bearing ones. In v8 the split result was never handed toclassList.add()— it only fed a filter — so malformed tokens were harmless.Suggested fix
We fixed this in our own templates by keeping class attributes on one line, and additionally carry the split fix in a local
pnpm patchas insurance.Found while upgrading one app from 8.8.19 to 9.0.0; two more regressions from the same release: #31392 (cumulative
SetinsyncElementClassesresurrects removed conditional classes) and #31394 (ion-radiovalue reflect).