Skip to content

perf(Button/Select/SelectMenu/InputMenu): narrow reactive dependencies#6736

Open
benjamincanac wants to merge 4 commits into
v4from
perf/narrow-icon-props
Open

perf(Button/Select/SelectMenu/InputMenu): narrow reactive dependencies#6736
benjamincanac wants to merge 4 commits into
v4from
perf/narrow-icon-props

Conversation

@benjamincanac

@benjamincanac benjamincanac commented Jul 15, 2026

Copy link
Copy Markdown
Member

🔗 Linked issue

Related to #6293 and #4154

❓ Type of change

  • 👌 Enhancement (improving an existing functionality)

📚 Description

Profiling a Button re-render showed that most of its cost came from over-broad reactivity, not from rendering itself. Button fed useComponentIcons a { ...props } spread, which walks every prop through the useComponentProps proxy (with its camelCase/kebabCase checks per key) and subscribes the icons computed, and through isLeading/isTrailing the ui computed, to every single prop. Any prop change, even class, re-ran the whole tv pipeline. Select, SelectMenu and InputMenu had the same issue through defu(props, ...), which also copies every prop. Button additionally re-ran omit(linkProps, ...) in the template on every render, walking ~25 forwarded link keys through three proxy layers.

This narrows the icons input to the props the composable actually reads (an explicit !== undefined check keeps trailingIcon: null behaving exactly like defu did) and memoizes Button's forwarded link props in a computed.

Also adds two benchmarks: a Button link vs no-link comparison and a layer decomposition of the Button stack (plain button → reka PrimitiveULinkBaseULinkUButton), which is how this was found — the Link/LinkBase layers turned out to be ~2% of self-time, while Button's own reactivity accounted for most of the rest.

Measured with the new benches: Button re-render goes from 2,574 to 4,103 ops/s (388µs → 244µs per toggle cycle). Output classes are byte-identical: full suite passes with zero snapshot changes.

This should noticeably help cases like the #6293 editor toolbar (25 buttons re-rendering per transaction), though that issue likely also needs re-render reduction in the toolbar itself.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Runtime components now narrow reactive inputs passed to useComponentIcons, preserve trailing-icon defaults, and memoize forwarded link props in Button.vue. New Vitest benchmark suites measure mounting and re-rendering for plain buttons, links, router links, and Nuxt UI components with explicit setup and teardown.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: narrowing reactive dependencies across Button, Select, SelectMenu, and InputMenu.
Description check ✅ Passed The description is clearly related to the changeset and explains the performance motivation, implementation, and benchmarks.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/narrow-icon-props

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.

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/@nuxt/ui@6736

commit: 6262c8d

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.

1 participant