You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have searched for existing issues that already report this problem, without success.
Stencil Version
4.43.5 (every release since 4.33.0 is affected, see below)
Current Behavior
If a project uses a globalScript and that script (or anything it imports) creates a @stencil/store store at module scope, components stop re-rendering on store changes in dist/www lazy builds. The store itself keeps working: values update, onChange/on('set') callbacks fire. Only the part that re-renders subscribed components is gone. There is no error or warning anywhere, which makes this really nasty to track down — in our app it shipped unnoticed and we only caught it because theme switching visibly broke.
What's happening, as far as I can tell:
Since 4.33.0 the client runtime imports globalStyles from @stencil/core/internal/app-globals (added in #6268, src/utils/shadow-root.ts). app-globals is the same generated module that wraps the user's globalScript, so this creates a module cycle:
Rollup resolves the cycle by emitting the runtime module last in the chunk. @stencil/store checks typeof getRenderingRef !== 'function' once, inside createStore(). When the store is created during evaluation of the globalScript import graph, getRenderingRef is still an unassigned hoisted var at that point, so the check fails and the store silently skips the renderer subscription.
In the emitted dev bundle you can see the order directly: the store package evaluates at ~2.7k into the chunk, the store is created at ~7.9k, and var getRenderingRef = () => renderingRef is only assigned at ~130k. On 4.32.0 and older the order is correct (runtime first) and everything works.
Two things that make it worse:
It happens even with no globalStyle configured. BUILD flags are runtime values in the emitted chunk, so the import isn't tree-shaken.
Components subscribed to a store re-render when the store changes, regardless of whether the store module happens to be reachable from the globalScript import graph. This worked up to and including 4.32.0.
System Info
System: node 24.15.0
Platform: darwin (25.5.0)
CPU Model: Apple M4 Pro (14 cpus)
Stencil: 4.43.5
TypeScript: 5.8.3
Rollup: 4.44.0
Parse5: 7.2.1
jQuery: 4.0.0-pre
Terser: 5.37.0
Steps to Reproduce
The repro is ~40 lines: a store.ts with createStore at module scope (same pattern as the @stencil/store readme), a canonical default-exported globalScript that imports the store, and one component that renders store state.
npm install && npm run build
Serve www/ (e.g. npx serve www) and open it — the page has buttons and an expected-vs-actual explanation built in
The label shows "ready", proving the globalScript ran
Click "Increment store counter" a few times — the counter stays at 0
Click "Force unrelated re-render (sets a prop)" — the counter jumps to the number of clicks, so the store updated the whole time and only the re-render is missing
Check out the works-on-4.32 branch (identical code, @stencil/core pinned to 4.32.0), npm install && npm run build, repeat: the counter updates on every click
Timeline I verified against npm: internal/client has no app-globals reference up to 4.32.0, the import appears in 4.33.0 and is present in every release through 4.43.5. The 5.0.0-alpha line doesn't have it anymore (internal/app-globals is gone entirely there), so v5 seems to fix this as a side effect of the restructuring.
give globalStyles its own virtual module so the runtime never imports the globalScript graph (which appears to be what the v5 alphas already do), or
in @stencil/store, resolve getRenderingRef/forceUpdate lazily inside the subscription handlers instead of once at createStore() time — by the time anything renders they're always assigned. (Happy to open that as a PR on stenciljs/store instead if you'd prefer the fix there.)
Our current workaround is a createStore wrapper that re-attaches an equivalent renderer subscription through the public store.use() API when it detects the built-in one was dropped.
Full disclosure: we used an AI assistant to help with the version bisecting and bundle analysis during this investigation, but the reproduction and all results above were verified by hand.
Prerequisites
Stencil Version
4.43.5 (every release since 4.33.0 is affected, see below)
Current Behavior
If a project uses a
globalScriptand that script (or anything it imports) creates a@stencil/storestore at module scope, components stop re-rendering on store changes indist/wwwlazy builds. The store itself keeps working: values update,onChange/on('set')callbacks fire. Only the part that re-renders subscribed components is gone. There is no error or warning anywhere, which makes this really nasty to track down — in our app it shipped unnoticed and we only caught it because theme switching visibly broke.What's happening, as far as I can tell:
Since 4.33.0 the client runtime imports
globalStylesfrom@stencil/core/internal/app-globals(added in #6268,src/utils/shadow-root.ts).app-globalsis the same generated module that wraps the user'sglobalScript, so this creates a module cycle:Rollup resolves the cycle by emitting the runtime module last in the chunk.
@stencil/storecheckstypeof getRenderingRef !== 'function'once, insidecreateStore(). When the store is created during evaluation of the globalScript import graph,getRenderingRefis still an unassigned hoistedvarat that point, so the check fails and the store silently skips the renderer subscription.In the emitted dev bundle you can see the order directly: the store package evaluates at ~2.7k into the chunk, the store is created at ~7.9k, and
var getRenderingRef = () => renderingRefis only assigned at ~130k. On 4.32.0 and older the order is correct (runtime first) and everything works.Two things that make it worse:
globalStyleconfigured.BUILDflags are runtime values in the emitted chunk, so the import isn't tree-shaken.extras: { addGlobalStyleToComponents: false }(feat(global-styles): newaddGlobalStyleToComponentsextras option. Opt-out of new globalStyle behaviour #6292) doesn't help — it empties the styles string but the import edge stays.Expected Behavior
Components subscribed to a store re-render when the store changes, regardless of whether the store module happens to be reachable from the globalScript import graph. This worked up to and including 4.32.0.
System Info
System: node 24.15.0 Platform: darwin (25.5.0) CPU Model: Apple M4 Pro (14 cpus) Stencil: 4.43.5 TypeScript: 5.8.3 Rollup: 4.44.0 Parse5: 7.2.1 jQuery: 4.0.0-pre Terser: 5.37.0Steps to Reproduce
The repro is ~40 lines: a
store.tswithcreateStoreat module scope (same pattern as the @stencil/store readme), a canonical default-exportedglobalScriptthat imports the store, and one component that renders store state.npm install && npm run buildwww/(e.g.npx serve www) and open it — the page has buttons and an expected-vs-actual explanation built inworks-on-4.32branch (identical code,@stencil/corepinned to 4.32.0),npm install && npm run build, repeat: the counter updates on every clickCode Reproduction URL
https://github.com/CalinaCristian/stencil-store-globalscript-repro (
master= broken on 4.43.5,works-on-4.32branch = same code on 4.32.0, working)Additional Information
internal/clienthas noapp-globalsreference up to 4.32.0, the import appears in 4.33.0 and is present in every release through 4.43.5. The 5.0.0-alpha line doesn't have it anymore (internal/app-globalsis gone entirely there), so v5 seems to fix this as a side effect of the restructuring.dist-lazysibling of bug: externalRuntime flag makes @stencil/store changes no trigger render #6040 / bug: Store changes doesn't trigger re-render of a component ondist-custom-elementsbuild #4135, which were the same "store silently stops re-rendering" failure fordist-custom-elements, fixed in fix(dist-custom-elements): stop duplicate@stencil/core#6109.globalStylesits own virtual module so the runtime never imports the globalScript graph (which appears to be what the v5 alphas already do), or@stencil/store, resolvegetRenderingRef/forceUpdatelazily inside the subscription handlers instead of once atcreateStore()time — by the time anything renders they're always assigned. (Happy to open that as a PR on stenciljs/store instead if you'd prefer the fix there.)createStorewrapper that re-attaches an equivalent renderer subscription through the publicstore.use()API when it detects the built-in one was dropped.