Skip to content

feat(core): update component styles in-place during hmr - #69964

Draft
clydin wants to merge 1 commit into
angular:mainfrom
clydin:feat/hmr-inplace-style-updates
Draft

feat(core): update component styles in-place during hmr#69964
clydin wants to merge 1 commit into
angular:mainfrom
clydin:feat/hmr-inplace-style-updates

Conversation

@clydin

@clydin clydin commented Jul 27, 2026

Copy link
Copy Markdown
Member

During hot module replacement (HMR), updating component inline styles
(<style>) or external stylesheet URLs (<link rel="stylesheet">) previously
triggered a complete destruction and recreation of matching component view
trees (LView). This caused loss of live application state (such as input
values, scroll positions, component signal state, and iframe instances) whenever
a CSS or SCSS file was edited.

This change introduces replaceStyles on SharedStylesHost, allowing HMR to
mutate inline style textContent or link href attributes in-place. The method
accepts an isScoped flag (oldDef.encapsulation !== ViewEncapsulation.None)
to distinguish component-scoped styles from un-encapsulated styles:

  • For scoped styles (isScoped === true), in-place mutation and tracking
    record re-keying runs regardless of record.usage, preserving active instance
    usage counts so that multi-instance components unmount cleanly without premature
    style removal.
  • For unscoped styles (isScoped === false), if record.usage > 1, it falls
    back to removeUsage + addUsage to protect other component classes sharing
    identical un-encapsulated initial CSS.

If non-style metadata (such as template, inputs, outputs, providers,
hostBindings, selectors, or hostDirectives) or encapsulation (ShadowDom)
changes alongside styles, HMR safely falls back to view tree recreation.

@angular-robot angular-robot Bot added detected: feature PR contains a feature commit area: core Issues related to the framework runtime labels Jul 27, 2026
@ngbot ngbot Bot added this to the Backlog milestone Jul 27, 2026
@clydin
clydin force-pushed the feat/hmr-inplace-style-updates branch 2 times, most recently from 1ebb122 to d5ff82a Compare July 27, 2026 23:28
During hot module replacement (HMR), updating component inline styles
(`<style>`) or external stylesheet URLs (`<link rel="stylesheet">`) previously
triggered a complete destruction and recreation of matching component view
trees (`LView`). This caused loss of live application state (such as input
values, scroll positions, component signal state, and iframe instances) whenever
a CSS or SCSS file was edited.

This change introduces `replaceStyles` on `SharedStylesHost`, allowing HMR to
mutate inline style `textContent` or link `href` attributes in-place. The method
accepts an `isScoped` flag (`oldDef.encapsulation !== ViewEncapsulation.None`)
to distinguish component-scoped styles from un-encapsulated styles:
- For scoped styles (`isScoped === true`), in-place mutation and tracking
  record re-keying runs regardless of `record.usage`, preserving active instance
  usage counts so that multi-instance components unmount cleanly without premature
  style removal.
- For unscoped styles (`isScoped === false`), if `record.usage > 1`, it falls
  back to `removeUsage` + `addUsage` to protect other component classes sharing
  identical un-encapsulated initial CSS.

If non-style metadata (such as `template`, `inputs`, `outputs`, `providers`,
`hostBindings`, `selectors`, or `hostDirectives`) or encapsulation (`ShadowDom`)
changes alongside styles, HMR safely falls back to view tree recreation.
@clydin
clydin force-pushed the feat/hmr-inplace-style-updates branch from d5ff82a to 39644aa Compare July 27, 2026 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Issues related to the framework runtime detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant