New Jun 12, 2025

How to make a custom dropdown inside a scrollable popup behave like a native (i.e., render above content without expanding the container)?

Libraries, Frameworks, etc. All from Newest questions tagged reactjs - Stack Overflow View How to make a custom dropdown inside a scrollable popup behave like a native (i.e., render above content without expanding the container)? on stackoverflow.com

I'm building a custom searchable dropdown component in React that works fine in general — but I'm facing a layout issue when it's used inside a popup modal/form.

Here’s the problem:

The popup form has overflow: auto (to allow scrolling).

My dropdown is placed near the bottom of the popup form.

When the dropdown opens, instead of rendering over the rest of the UI (like a native would), it pushes the popup content down, expanding the height of the scrollable container.

This creates a weird UX — the dropdown feels like it’s breaking the layout instead of floating naturally. ill drop my search Drop down component and css

Search_Drop_Down.jsx

import React, { useState, useRef, useEffect } from "react";
import "./CustomDropdown.css";
import { Icon } from "@iconify/react";

const SearchDD = ({ options = [], // expects [{ id: "", name: "" }] placeholder = "Select an option", onSelect, selectedPart, // expects id }) => { const [search, setSearch] = useState(""); const [isOpen, setIsOpen] = useState(false); const [filteredOptions, setFilteredOptions] = useState(options); const dropdownRef = useRef(null);

useEffect(() => { setFilteredOptions( options.filter((option) => option.name.toLowerCase().includes(search.toLowerCase()) ) ); }, [search, options]);

useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []);

const handleSelect = (option) => { setSearch(""); setIsOpen(false); if (onSelect) onSelect(option.id); // return id };

const selectedOption = options.find((opt) => opt.id === selectedPart);

return ( <div className="custom-dropdown" ref={dropdownRef}> <div className="dropdown-selected" onClick={() => setIsOpen(!isOpen)}> {selectedOption ? selectedOption.name : placeholder} <span className="arrow"> {isOpen ? ( <Icon icon="solar:alt-arrow-up-linear" height={20} /> ) : ( <Icon icon="solar:alt-arrow-down-linear" height={20} /> )} </span> </div>

{isOpen && ( <div className="dropdown-menu"> <input className="dropdown-search" type="text" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search..." /> <div className="dropdown-options"> {filteredOptions.length > 0 ? ( filteredOptions.map((option) => ( <div key={option.id} className="dropdown-option" onClick={() => handleSelect(option)} > {option.name} </div> )) ) : ( <div className="dropdown-option no-option">No options found</div> )} </div> </div> )} </div> ); };

export default SearchDD;

Css:

.custom-dropdown {
  position: relative;
  max-width: 400px;
  font-family: var(--ff-primary);
}

.dropdown-selected { border: 1px solid var(--border-clr); border-radius: var(--border-radius); padding: 0.5rem 0.75rem; background-color: var(--theme-bg); cursor: pointer; display: flex; justify-content: space-between; align-items: center; box-shadow: var(--box-shadow); font-weight: var(--fw-400); }

.arrow { font-size: 0.75rem; color: var(--theme-primary); }

.dropdown-menu { position: absolute; width: 100%; max-height: 250px; overflow-y: auto; margin-top: 0.25rem; background: var(--theme-bg); border: 1px solid var(--border-clr); border-radius: var(--border-radius); box-shadow: var(--box-shadow); z-index: 99999; }

.dropdown-search { position: sticky; top: 0; width: 100%; padding: 0.5rem 1rem; border: none; border-bottom: 1px solid var(--border-clr); background-color: var(--theme-secondary); font-size: var(--fs-100); outline: none; }

.dropdown-options { max-height: 200px; overflow-y: auto; }

.dropdown-option { padding: 0.75rem 1rem; cursor: pointer; transition: background 0.2s; }

.dropdown-option:hover { background-color: var(--theme-secondary); }

.no-option { color: gray; font-style: italic; }

What I’ve tried: Setting a high z-index → didn’t help because it’s still inside the scrollable container. Tried position: absolute and fixed, but it still gets clipped or affects layout. Moving the dropdown options container outside the popup seems complicated, especially with positioning logic.

What I want: I want the dropdown list (options) to render above the form content, or escape the scrollable popup, just like how a native opens on top of everything else without affecting layout.

Scroll to top