fix: render custom element arrays as a Fragment to avoid remounting on re-render - #531
Merged
Conversation
added 2 commits
August 19, 2026 14:33
…n re-render. renderCustomElements() is documented for `<component :is="renderCustomElements(page.content)" />`, which re-invokes it on every render of the consuming component. For an array payload it returned a freshly created anonymous component on each call, so `<component :is>` saw a new component type every render and unmounted then remounted the entire custom-element subtree on any unrelated re-render — discarding component state, focus and scroll position (single-element payloads, returned as a plain VNode, were unaffected). Return the VNode[] wrapped in a Fragment instead. A Fragment has a stable type, so a re-render patches the children in place rather than remounting, while still reflecting content changes. `<component :is>` accepts a VNode, so the template usage is unchanged.
fago
approved these changes
Aug 19, 2026
Contributor
|
I verified the test fails with the fix, good improvement. |
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.
Closes #530.
Problem
renderCustomElements()is documented for<component :is="renderCustomElements(page.content)" />, which Vue re-evaluates on every render of the consuming component. For an array payload it returned a freshly created anonymous component on each call, so<component :is>saw a new component type every render and unmounted then remounted the entire custom-element subtree on any unrelated re-render — discarding component state, focus and scroll position. Single-element payloads (returned as a plainVNode) were unaffected, which makes the bug easy to miss.Fix
Wrap the
VNode[]in a Fragment instead of a newdefineComponent:A Fragment has a stable type, so a re-render patches the children in place rather than remounting, while still reflecting content changes.
<component :is>accepts aVNode, so the documented template usage is unchanged and the rendered markup is identical (a Fragment emits its children with no wrapper element). The return type narrows fromVNode | Component | nulltoVNode | null.Test
Adds a regression test to
test/nuxt/renderCustomElements.test.tsthat renders an array payload the documented way, bumps an unrelated ref to force a parent re-render, and asserts a mount-counting child is not remounted. It fails on the old code (mount count 2) and passes with this fix (mount count 1). The existing array-rendering assertions (markup output, empty arrays) are unchanged.Notes
Drafted with assistance from Claude Code.