JedWatson / JedWatson/react-select
Menu height not recalculated when async options load, causing truncated dropdown with menuPlacement="auto"
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 28k
- Forks
- 4.1k
- PR merge metrics
- No merged PRs in 30d
Description
Bug Description
When using react-select with async data loading and menuPlacement="auto", the dropdown menu height is calculated based on the loading state (empty or with loading indicator) and is not recalculated once the actual options load. This results in the dropdown being truncated or options being hidden below the viewport.
Expected Behavior
The dropdown menu should recalculate its position and height after async options are loaded to ensure all options are visible and properly positioned.
Actual Behavior
The menu height/position is calculated once when isLoading={true} (with minimal content), and remains fixed even after options load, causing content to be cut off.
Minimal Reproduction
import Select from 'react-select';
import { useState, useEffect } from 'react';
const AsyncSelectExample = () => {
const [options, setOptions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// Simulate async data loading
setTimeout(() => {
setOptions([
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
// ... 20+ options to demonstrate the issue
]);
setIsLoading(false);
}, 2000);
}, []);
return (
<div style={{ marginTop: '400px' }}> {/* Position near bottom to trigger the issue */}
<Select
options={options}
isLoading={isLoading}
menuPlacement="auto"
placeholder="Select an option..."
/>
</div>
);
};
Steps to Reproduce
- Create a Select component with menuPlacement="auto"
- Position it near the bottom of the viewport
- Set isLoading={true} initially with empty options
- Load options asynchronously after a delay
- Open the dropdown - notice it's truncated/positioned incorrectly
Environment
- react-select version: 5.8.3
- React version: 18.2.0
- Browser: Chrome v138.0.7204.101 (Official Build) (arm64)
- OS: Mac
Workaround
I've implemented a custom hook that recalculates menu placement on menu open:
// Custom hook to handle dynamic menu placement
const useMenuPlacement = ({ maxHeight, selectRef, onMenuOpen, onMenuClose }) => {
const [menuPlacement, setMenuPlacement] = useState('bottom');
const [dynamicMaxHeight, setDynamicMaxHeight] = useState(maxHeight);
const handleMenuOpen = useCallback(() => {
// Recalculate placement based on current viewport space
const selectElement = selectRef.current?.controlRef;
if (selectElement) {
const selectRect = selectElement.getBoundingClientRect();
const spaceBelow = window.innerHeight - selectRect.bottom;
const spaceAbove = selectRect.top;
if (spaceBelow > 200) {
setMenuPlacement('bottom');
setDynamicMaxHeight(spaceBelow - 80);
} else if (spaceAbove > 200) {
setMenuPlacement('top');
setDynamicMaxHeight(spaceAbove - 80);
} else {
setMenuPlacement(spaceBelow > spaceAbove ? 'bottom' : 'top');
setDynamicMaxHeight(Math.max(spaceBelow, spaceAbove));
}
}
onMenuOpen?.();
}, [selectRef, onMenuOpen]);
return {
menuPlacement,
dynamicMaxHeight,
onMenuOpen: handleMenuOpen,
// ... other returns
};
};
Potential Solution
The issue seems to be that the menu positioning calculation happens once when the menu opens, but doesn't account for content changes. The library should:
- Recalculate menu placement when options change from loading to loaded state
- Or provide a way to trigger menu repositioning programmatically
- Or improve the menuPlacement="auto" logic to be more dynamic
Would be happy to contribute a PR if guidance is provided on the preferred approach.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the Select component's menuPlacement="auto" positioning path and reproduce the issue with the provided async-loading example near the bottom of the viewport. Trace when menu height and placement are calculated, then verify that loading-to-loaded options remain fully visible and correctly positioned.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100