refactor(web): typing no longer re-renders the composer model picker#22721
Every keystroke in the composer re-rendered the model picker and the attach button, even though neither depends on the prompt.
The fix uses component boundaries, not stable-callback wrappers. This adds no useStableCallback uses; ChatComposer keeps the 34 it has on main.
ComposerModelPicker(inComposerFooterControls.tsx) wrapsProviderModelPicker. It defines the single-model and multi-model toggle handlers itself, from the multi-model selections and their setter,selectedModelSelection, the instance entries, the model options andonProviderModelSelect. None of those change while you type, so the compiler keeps the handlers. ChatComposer builds the<ComposerModelPicker>element as its own const. The controls row around it is rebuilt on every keystroke becauseTraitsPickertakesprompt, but the picker element itself stays cached.ComposerAttachButtonowns the hidden file input and builds its change handler fromaddComposerAttachmentsandfocusComposer.
| main | this PR | |
|---|---|---|
| ChatComposer.tsx lines | 6,700 | 6,640 |
| Renders, 20 typed chars | 678 (22 commits) | 352 (22 commits) |
| ProviderModelPicker subtree renders | 21 + popover/tooltip children | 0 |
| Attach button renders | 21 (button, tooltip, icon) | 21 (ComposerAttachButton alone) |
Renders measured with the shared fiber counter (rc-init.js) on a seeded dev server: typing 20 characters into an existing thread's composer, median of 3 runs.
Still rendering on each keystroke:
ComposerAttachButtonitself.addComposerAttachmentsgets a new identity on every keystroke because of the prompt-editing helpers it calls. Its tooltip, button and icon children no longer re-render.TraitsPicker. It takespromptto detect "ultrathink", which needs a change to TraitsPicker itself. Both are left for follow-ups.
Tests run: vp lint and vp fmt --check on the changed files, tsc-rs --noEmit for apps/web, and reactCompilerCoverage.test.ts for the changed files (0 bailouts).
🤖 Generated with Claude Code
Checks
- SuccessfulCodeRabbitGitHub
Review completed
- Successful
Skipped - Not affected
- Skipped
- Skipped
- Skipped
- SuccessfulRemove preview downloadGitHub
- SuccessfulLabel PR 22721GitHub
- Skipped
- SuccessfulLabel PR sizeGitHub
- Successful
- Successful
- SuccessfulPrepare PR size configGitHub
- Successful
- SuccessfulCollect PR targetsGitHub
- SuccessfulCheckGitHub
- SuccessfulNative fingerprint diffGitHub
- Skipped
- SkippedRustGitHub
- SkippedRelease SmokeGitHub
- SuccessfulTestGitHub
- SuccessfulBuild and TypecheckGitHub
- SuccessfulTest WebGitHub
- SkippedDeploy web previewGitHub
- SkippedEAS PreviewGitHub
- SuccessfulLabel PR 22721GitHub
- Skipped
- SuccessfulLabel PR sizeGitHub
- SuccessfulPrepare PR size configGitHub
- SuccessfulCollect PR targetsGitHub
- SuccessfulTest Server 4GitHub
- SuccessfulTest Server 6GitHub
- SuccessfulTest Server 1GitHub
- SuccessfulTest Server 7GitHub
- SuccessfulTest Server 8GitHub
- SuccessfulTest Server 3GitHub
- SuccessfulTest Server 2GitHub
- SuccessfulTest Server 5GitHub
- SuccessfulLintGitHub
- SuccessfulChangesGitHub
- SuccessfulNative fingerprint diffGitHub
Added 1 commit
- refactor(web): typing no longer re-renders the composer model pickereb67900
Approvability
Verdict: Approved at eb67900
Macroscope's review found this PR approvable — This is a localized web UI performance refactor that extracts existing model-picker and attachment-control logic without changing product defaults, APIs, or workflows. The runtime impact is limited and the existing behavior is preserved across the three touched components.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review. 🎉
ℹ️ Recent review info
⚙️ Run configuration
- Configuration used: Path: .coderabbit.config.ts
- Review profile: CHILL
- Plan: Team
- Run ID:
29f60211-d5b6-4b2c-ba6d-c286ca507d3c
📥 Commits
Reviewing files that changed from the base of the PR and between 8c483f867955bff4383fe6014589cab135984d68 and eb67900ea5954f10f1a98adeac3f7c12716a099a.
📒 Files selected for processing: 3
apps/web/src/components/chat/ChatComposer.tsxapps/web/src/components/chat/ComposerAttachmentStrip.tsxapps/web/src/components/chat/ComposerFooterControls.tsx
Limit details: You’ve used all 10 included reviews currently available.
📝 Walkthrough
ChatComposer now uses extracted components for model selection and file attachments. The model picker supports single- and multi-model selection. The attachment button handles file selection and refocuses the composer when no attachments are inserted.
Changes: Composer controls
Layer / File(s) Summary Model picker behavior and integration
apps/web/src/components/chat/ComposerFooterControls.tsx,apps/web/src/components/chat/ChatComposer.tsxComposerModelPickeradapts model toggles and direct model changes for single- and multi-model selection.ChatComposerconfigures and renders the component.Attachment button behavior and integration
apps/web/src/components/chat/ComposerAttachmentStrip.tsx,apps/web/src/components/chat/ChatComposer.tsxComposerAttachButtonopens a multi-file picker, resets its input after selection, and refocuses the composer only when no attachments were inserted.ChatComposerrenders the extracted button.Priority: ⬇️ Low
Estimated code review effort: 2 (Simple) | ~15 minutes
Change: Refactor
Merge Risk: ⚪ Minimal · up to eb679
The composer controls retain their selection and attachment behavior. No identified issue prevents merging after normal checks.
Pre-merge checks: Passed 3 · Failed 1
Failed Failed checks: 1 warning
Check name Status Explanation Resolution Description check Warning The description explains the problem, implementation, measured results, remaining renders, and verification. It does not follow the required headings, provide the required issue or maintainer approval… Add the required Problem, Change, Scope and approval, and Verification sections. Link the triaged issue or explicit maintainer approval, or explain the applicable exemption. Add clear before-and-after screenshots and any needed interaction … Passed Passed checks: 3 passed
Check name Status Explanation Title check Passed The title clearly and concisely describes the main web refactor: typing no longer re-renders the composer model picker. 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.
Full details: Description check
Explanation
The description explains the problem, implementation, measured results, remaining renders, and verification. It does not follow the required headings, provide the required issue or maintainer approval evidence, or include before-and-after screenshots for this UI change.
Resolution
Add the required Problem, Change, Scope and approval, and Verification sections. Link the triaged issue or explicit maintainer approval, or explain the applicable exemption. Add clear before-and-after screenshots and any needed interaction recording. Preserve the reported test commands and results.
✨ Finishing Touches
📝 Generate docstrings
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
- Commit to this branch
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Usage-based review receipt
- Mode: Continue automatically
- Reviewed files: 3
- Charged: $0.75
- View usage details
Note
This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.
Comment @coderabbitai help to get the list of available commands.