When a createPortal container holds <style precedence> elements and is later removed from the DOM, React's hoistableStyles map keeps references to the destroyed <style> nodes and never re-creates them. Any component depending on those styles loses them for the rest of the session, even rendering in a connected tree.
React version: 19.0.0 through 19.2.5, and canary 19.3.0-canary-ad5dfc82-20260427
Steps To Reproduce
- Render a component using
<style href="x" precedence="custom"> inside a createPortal whose container is a detached div
- Render the same component (same href) directly in the main tree
- Remove the portal container from the DOM
- The directly-rendered component loses its styles and never gets them back
Link to code example: https://stackblitz.com/edit/vitejs-vite-fkhvowjt
The current behavior
React inserts the <style> into the portal container because getRootNode() on a detached element returns itself. When a second instance of the same resource renders in the main tree, React sees the existing hoistableStyles entry and reuses it, but the actual element still lives inside the portal container. Removing that container destroys the <style> node. The map still points to the dead node (isConnected: false, parentNode: DIV), so React never re-creates it. Styles are gone.
The expected behavior
Removing a portal container should not destroy style resources used by components elsewhere in the tree. acquireResource should check whether a cached style instance is still connected and re-create it if not.
When a
createPortalcontainer holds<style precedence>elements and is later removed from the DOM, React'shoistableStylesmap keeps references to the destroyed<style>nodes and never re-creates them. Any component depending on those styles loses them for the rest of the session, even rendering in a connected tree.React version:
19.0.0through19.2.5, and canary19.3.0-canary-ad5dfc82-20260427Steps To Reproduce
<style href="x" precedence="custom">inside acreatePortalwhose container is a detached divLink to code example: https://stackblitz.com/edit/vitejs-vite-fkhvowjt
The current behavior
React inserts the
<style>into the portal container becausegetRootNode()on a detached element returns itself. When a second instance of the same resource renders in the main tree, React sees the existinghoistableStylesentry and reuses it, but the actual element still lives inside the portal container. Removing that container destroys the<style>node. The map still points to the dead node (isConnected: false, parentNode: DIV), so React never re-creates it. Styles are gone.The expected behavior
Removing a portal container should not destroy style resources used by components elsewhere in the tree.
acquireResourceshould check whether a cached style instance is still connected and re-create it if not.