fix(utils): single global stylesheet instance for performance#6320
Merged
christian-bromann merged 3 commits intostenciljs:mainfrom Jun 29, 2025
Merged
fix(utils): single global stylesheet instance for performance#6320christian-bromann merged 3 commits intostenciljs:mainfrom
christian-bromann merged 3 commits intostenciljs:mainfrom
Conversation
aeharding
commented
Jun 27, 2025
2 tasks
aeharding
added a commit
to aeharding/voyager
that referenced
this pull request
Jun 27, 2025
aeharding
added a commit
to aeharding/voyager
that referenced
this pull request
Jun 27, 2025
src/utils/shadow-root.ts
Outdated
| * This singleton avoids the performance and memory hit of | ||
| * creating a new CSSStyleSheet every time a shadow root is created. | ||
| */ | ||
| export const globalStyleSheet = createStyleSheetIfNeededAndSupported(globalStyles); |
Member
There was a problem hiding this comment.
It seems like the way the runtime is bundled doesn't allow to initiate it at the root level? Maybe we can do something like:
let globalStyleSheet
export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeMeta) {
const shadowRoot = BUILD.shadowDelegatesFocus
? this.attachShadow({
mode: 'open',
delegatesFocus: !!(cmpMeta.$flags$ & CMP_FLAGS.shadowDelegatesFocus),
})
: this.attachShadow({ mode: 'open' });
/**
* If constructable stylesheets are supported, we can use them to
* add the global styles to the shadow root.
*/
if (supportsConstructableStylesheets) {
const sheet = new CSSStyleSheet();
sheet.replaceSync(globalStyles);
shadowRoot.adoptedStyleSheets.push(sheet);
}
if (!globalStyleSheet) {
globalStyleSheet = createStyleSheetIfNeededAndSupported(globalStyles)
shadowRoot.adoptedStyleSheets.push(globalStyleSheet)
};
}
Contributor
Author
There was a problem hiding this comment.
Interesting! I wonder why it worked in Voyager but not the test environment. I just added lazy initialization. Although, if it's possible rejigger the build to avoid that error in the first place, that would probably be a cleaner solution than the lazy initializer.
Lmk what you think!
Contributor
Author
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the current behavior?
GitHub Issue Number: None
Since Ionic v8.6.0 I've been experiencing component instance creation performance issues, especially in virtual lists, where it can take items longer to appear. This was originally identified by end users: https://vger.social/post/20787019
I narrowed it down to the stencil upgrade here.
From there I found the regression occurring in Stencil v4.33.0 specifically. When I reverted 33363d4077728793e0c6f635a22dccbb5740be49 the problem went away.
Once I identified the source of the issue, I found some suspicious code that was creating a new
CSSStyleSheetevery time a new component instance is created.What is interesting is this performance issue exists even if
globalStylesis empty. Simply creating a newCSSStyleSheetsand callingreplaceSyncon an empty string every instantiation is enough to hurt performance.What is the new behavior?
I have modified the code to only create one global
CSSStyleSheet(if necessary) on startup that is appended toadoptedStyleSheetsin thecreateShadowRootfunction.This solves performance issues of using
globalStyles. In addition, I added an early return to not create the global style sheet at all, ifglobalStylesis empty. This doesn't seem to affect perf, just makes the DX a bit nicer by not having a useless/emptyCSSStyleSheetin theadoptedStyleSheetsarray.Documentation
Does this introduce a breaking change?
Testing
I tested these changes against Voyager by building stencil, building ionic, and finally installing in Voyager.
Other information