Add virtualization support for Combobox and ButtonSelect#6113
Add virtualization support for Combobox and ButtonSelect#6113
Conversation
- Add @tanstack/react-virtual for efficient rendering of large lists - Create shared useVirtualizedNavigation hook for keyboard navigation - Create VirtualizedCommandItems component for virtualized rendering - Add virtualizeThreshold prop (default: 100) to both components - Support ArrowUp/Down, Home/End, PageUp/PageDown keyboard navigation
- Add Combobox.stories.tsx with default, virtualized, and edge case stories - Add virtualization stories to ButtonSelect.stories.tsx - Stories cover: basic usage, virtualized mode, stress testing, keyboard nav
- Merges stories from simeonlee/combobox-virtualization-stories - Resolves merge conflict in ComboboxMenuItems.tsx - Combines core virtualization with Storybook stories
|
@codex review |
|
@cursor review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0307647c59
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Add hasCreateOption and onSelectCreate props to useVirtualizedNavigation hook - Index -1 now represents the create option when hasCreateOption is true - Update Combobox and ButtonSelect to pass create callbacks - Update ComboboxMenuItems to highlight create option when index is -1 - Fixes bug where Enter key couldn't select create option in virtualized mode
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0307647c59
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const handleKeyDown = useCallback( | ||
| (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
| if (shouldVirtualize) { | ||
| virtualizedHandleKeyDown(e); | ||
| // Still need base handler for opening dropdown on arrow keys |
There was a problem hiding this comment.
Avoid selecting item on
Enter when menu is closed
When shouldVirtualize is true, the input onKeyDown always forwards events to virtualizedHandleKeyDown even if the popover is closed. That handler selects on Enter, so focusing the combobox and pressing Enter can immediately select the first filtered item without ever opening the menu. In the non-virtualized path, Enter only opens the menu via baseHandleKeyDown, so this is a behavior regression for large lists (e.g., pressing Enter to submit a form now changes the selection). Consider gating the virtualized handler on open (or special-casing Enter when closed) so Enter behaves consistently.
Useful? React with 👍 / 👎.
When there are no items (e.g., all filtered out), End and PageDown could set highlightedIndex to an invalid value. Now they correctly return minIndex (-1 for create option, 0 otherwise) when itemCount is 0.
| import { useVirtualizedNavigation } from "~/components/ui/use-virtualized-navigation"; | ||
|
|
||
| /** Default threshold for enabling virtualization */ | ||
| const DEFAULT_VIRTUALIZE_THRESHOLD = 100; |
There was a problem hiding this comment.
Duplicate constant defined in two files
Low Severity
DEFAULT_VIRTUALIZE_THRESHOLD = 100 is defined identically in both Combobox.tsx and ButtonSelect.tsx. This constant should be defined once in a shared location (e.g., use-virtualized-navigation.ts or a separate constants file) and imported by both components.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| onSelectCreate: () => { | ||
| handleSelectItem(searchValue.trim(), true); | ||
| }, | ||
| }); |
There was a problem hiding this comment.
ButtonSelect missing onClose callback for Escape key
Medium Severity
The useVirtualizedNavigation hook call in ButtonSelect is missing the onClose callback, unlike Combobox which passes onClose: closeDropdown. The hook explicitly handles Escape key by calling onClose(), but since it's not provided, pressing Escape in virtualized mode won't close the dropdown through the custom handler. The fix would be to add onClose: () => setOpen(false) to match the intended keyboard navigation behavior.


Summary
@tanstack/react-virtualfor efficient rendering of large lists (100+ items)useVirtualizedNavigationhook for keyboard navigation (DRY)VirtualizedCommandItemscomponent for virtualized renderingvirtualizeThresholdprop (default: 100) to bothComboboxandButtonSelectScreenshots
Virtualized Dropdown (500 items)
Virtualized with Creation
Keyboard Navigation
Changes
use-virtualized-navigation.tsvirtualized-command-items.tsxComboboxMenuItems.tsxCombobox.tsxvirtualizeThresholdpropButtonSelect.tsxvirtualizeThresholdpropCombobox.stories.tsxButtonSelect.stories.tsxui.combobox-keyboard.spec.tsTest plan
Supersedes #5812 with DRY refactored code. Includes stories from #6116.
Note
Medium Risk
Touches shared dropdown keyboard handling and selection/highlighting behavior, so regressions could affect accessibility and basic picking flows even though the change is UI-only and gated behind a threshold.
Overview
Adds virtualization for large dropdown lists in
ComboboxandButtonSelectvia a newvirtualizeThresholdprop (default100,0=always,Infinity=never) and the@tanstack/react-virtualdependency.Introduces
VirtualizedCommandItemsfor virtualized rendering and a shareduseVirtualizedNavigationhook to provide consistent keyboard navigation (arrows, Home/End, PageUp/PageDown, Enter, Escape) with explicit highlighted state wired throughComboboxMenuItems.Expands Storybook coverage with virtualization/stress/keyboard demos and adds a Playwright spec validating combobox keyboard navigation and Escape-to-close behavior.
Written by Cursor Bugbot for commit 024bb07. This will update automatically on new commits. Configure here.