Dropdown MenuLoading saved progress…

Dropdown Menu

Build an accessible dropdown menu as a single React component: a trigger button that reveals a list of actions and hides it again. The interesting part isn't the open/close toggle — it's everything that should dismiss the menu (a click outside, the Escape key, choosing an item) and moving a keyboard highlight through the options. It's the same pattern behind every account menu, kebab menu, and select in real apps.

What you'll build

The starter App.tsx renders the trigger with the menu closed. Make it interactive:

  1. Toggle on click. Track open with useState. Clicking the trigger flips it; the <ul role="menu"> renders only when open is true, and aria-expanded mirrors it.
  2. Dismiss three ways. Close on: clicking an item, clicking anywhere outside the menu, and pressing Escape. Attach the document listeners in a useEffect and clean them up.
  3. Keyboard navigation. ArrowDown / ArrowUp move an activeIndex highlight through the four items (wrapping at the ends); Enter or Space activates the active item and closes the menu.

Examples

  • Click Options ▾ and the menu drops down with Profile, Settings, Help, Sign out. Click Settings and the menu closes.
  • With the menu open, click anywhere else on the page — it closes. Press Escape — it closes.
  • With the menu open, press ArrowDown a few times to walk the highlight down (and wrap from the last item back to the first), then Enter to activate.

Notes

  • Outside-click needs a document listener. The trigger's own onClick can't see clicks elsewhere; you listen on document and check whether the target is inside the wrapper.
  • Register the listener while open, and clean it up. A useEffect keyed on open adds the listeners when the menu opens and removes them when it closes or unmounts — no leaks.
  • ARIA is part of the spec. The trigger has aria-haspopup="menu" and aria-expanded; the list is role="menu" and each item role="menuitem".
  • Styling is already in styles.css; focus on the state and the listeners.
Loading editor…
Loading preview…