Skip to content

fix(Accordion/Tabs): use item value as stable key to avoid remounts#6380

Merged
benjamincanac merged 2 commits intonuxt:v4from
claylevering:fix/tabs-accordion-stable-keys
Apr 21, 2026
Merged

fix(Accordion/Tabs): use item value as stable key to avoid remounts#6380
benjamincanac merged 2 commits intonuxt:v4from
claylevering:fix/tabs-accordion-stable-keys

Conversation

@claylevering
Copy link
Copy Markdown
Contributor

🔗 Linked Issue

Closes #5841

📚 Description

Both UTabs and UAccordion currently key their v-for items by the iteration index:

<TabsTrigger v-for="(item, index) of items" :key="index" ... />
<TabsContent v-for="(item, index) of items" :key="index" ... />
<AccordionItem v-for="(item, index) in props.items" :key="index" ... />

When items are added, removed, or reordered, Vue sees "the same key at the same position," reuses the DOM element, and swaps its inner content — which unmounts any stateful descendant of the reused slot. For UTabs this means every tab after the changed position loses local state (counters, form inputs, subscriptions). For UAccordion the same pattern causes content remounts and — combined with lazy-loaded islands — occasional hydration oddness.

Switch both to :key="get(item, valueKey) ?? index" — the same expression already used for :value — so items with a user-provided value get a stable identity across reorders, while items without a value keep the previous index-based behaviour (no regression for existing users who don't provide value).

🔖 Checklist

  • Read contribution guide.
  • Installed dependencies with pnpm install.
  • Code is properly formatted and linted with pnpm lint --fix.
  • Made sure the docs build correctly with pnpm docs:build.
  • Made sure tests are passing with pnpm test.

Both `UTabs` and `UAccordion` used `:key="index"` when rendering their
`v-for` items. When items are added, removed, or reordered, Vue sees
"the same key at the same position," reuses the DOM element, and
swaps its inner content — which unmounts any stateful descendant of
the reused slot.

For `UTabs` this means every tab after the changed position loses
local state (counters, form inputs, subscriptions). For `UAccordion`
the same pattern causes content remounts and occasional hydration
oddness when combined with lazy-loaded islands.

Switch both to `:key="get(item, valueKey) ?? index"` — the same
expression already used for `:value` — so items with a user-provided
`value` get a stable identity across reorders, while items without a
value keep the previous index-based behaviour.

Closes nuxt#5841
@github-actions github-actions Bot added the v4 #4488 label Apr 21, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

Accordion and Tabs components were updated to use each item’s configured value as the Vue key instead of the loop index. The key derivation now uses get(item, props.valueKey as string) ?? index for rendering identity (while :value for AccordionItem remains get(item, props.valueKey as string) ?? String(index)). The change ensures component identity tracks stable per-item values when provided, reducing remounts on insertions, removals, or reordering.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description thoroughly explains the problem (index-based keying causing remounts), the solution (using stable item values as keys), and references the linked issue.
Linked Issues check ✅ Passed The pull request directly addresses issue #5841 by implementing the proposed solution: changing from index-based keys to value-based keys for both Tabs and Accordion components to preserve component identity across array mutations.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the index-based key issue in Tabs and Accordion components as specified in #5841; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely describes the main change: switching from index-based to value-based keys in Accordion and Tabs components to prevent unnecessary remounts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

npm i https://pkg.pr.new/@nuxt/ui@6380

commit: c41987d

Clarify in the `TabsItem.value` and `AccordionItem.value` JSDoc that the
value doubles as the Vue `key` for rendered items — consumers should
provide a stable value if they add, remove, or reorder items and want
per-item state preserved across those changes.
@benjamincanac benjamincanac changed the title fix(Tabs,Accordion): use item value as stable key to avoid remounts fix(Accordion/Tabs): use item value as stable key to avoid remounts Apr 21, 2026
Copy link
Copy Markdown
Member

@benjamincanac benjamincanac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! 😊

@benjamincanac benjamincanac merged commit 3cee610 into nuxt:v4 Apr 21, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UTabs remounts all following tabs when adding/removing a tab due to index-based keys

2 participants