--- title: "React useState Dropdown — CSS group-hover vs useState for Playwright" source: daily/2026-05-10.md updated: 2026-05-10 --- ## Problem Playwright `hover()` on a trigger element does not reliably open a React dropdown that uses `useState` for visibility: ```tsx // ❌ Playwright hover misses this const [open, setOpen] = useState(false) {open && } ``` Root cause: `useState` updates are asynchronous — they schedule a re-render. By the time Playwright checks for the dropdown element, the render cycle may not have completed, so `getByRole('menu')` returns nothing. ## Fix — CSS `group-hover:` Replace JS state with a pure-CSS hover using Tailwind's `group` / `group-hover:` utilities: ```tsx // ✅ Playwright hover works reliably
``` The dropdown is always in the DOM; `group-hover:block` just changes `display`. No render cycle, no timing issues — Playwright sees the element immediately after hover. ## Secondary Benefit CSS hover-driven dropdowns also fix the `overflow: hidden` clipping issue — the dropdown is rendered at the right DOM level and can be placed outside the clipping ancestor. See [[wiki/concepts/overflow-hidden-clips-absolute-children]]. ## When useState Is Still Needed - Keyboard accessibility (Escape to close, focus trapping) - Mobile (no hover events) with click-to-toggle - Complex open/close animations that require knowing state In those cases, add an explicit `await page.waitForSelector('[role=menu]')` in Playwright tests rather than relying on hover timing.