Skip to page content

QMenu

The QMenu component is a convenient way to show menus. Goes very well with QList as dropdown content, but it’s by no means limited to it. When the content is indeed a list of actions, declare it through role="menu" on the QList — see Accessibility.

Usage

The idea with QMenu is to place it inside your DOM element / component that you want to be the trigger as direct child. Don’t worry about QMenu content inheriting CSS from the container as the QMenu will be injected as a direct child of <body> through a Quasar Portal.

TIP

Don’t forget to use the directive v-close-popup in your clickable menu items if you want the menu to close automatically. Alternatively, you can use the QMenu’s property auto-close or handle closing the menu yourself through its v-model.

Basic

Basic



WARNING

If you want to conditionally activate or de-activate a QMenu, please use v-if on it instead of v-show.

Hover
v2.26+

With the hover prop the menu also opens when the pointer hovers its target and closes once the pointer has left both the target and the menu. The hover-hide-delay prop controls the grace period in which the pointer can travel between the two (or return) before the menu closes, while hover-delay postpones the opening.

Click/tap and keyboard interactions keep toggling the menu as usual, so touch devices (which have no hover) simply fall back to them; this also means that clicking the target (or activating it with Enter) while the menu is hover-shown closes it. The one exception is a click that lands while the menu is still animating into view: it is ignored, so a single move-and-click gesture on the target cannot close the menu that the very same gesture just opened. A hover-opened menu does not move keyboard focus onto itself. Submenus work too: hovering from a menu into a submenu opened from it keeps the whole chain open.

WARNING

The hover and context-menu props are mutually exclusive; when both are set, context-menu takes precedence and hover has no effect. Also, hover-triggered opens ignore touch-position, which keeps applying to click/tap opens only.

Hover



Sizing and styling

Sizing



Style



Context menu

You can also set QMenu to act as a context menu. A right click on the parent target triggers it, and so does a long press on touch-capable devices. A context menu opens on those interactions only, so the hover prop has no effect on it.

Persistent

If you want the QMenu to not close if app route changes or if hitting ESCAPE key or if clicking/tapping outside of the menu, then use persistent prop:

Transitions

In the example below there’s a few transitions showcased. For a full list of transitions available, go to Transitions.

Reusable

The example below shows how to create a re-usable menu that can be shared with different targets.

Positioning

The position of QMenu can be customized. It keeps account of the anchor and self optional props. The final position of QMenu popup is calculated so that it will be displayed on the available screen real estate, switching to the right-side and/or top-side when necessary.

For horizontal positioning you can use start and end when you want to automatically take into account if on RTL or non-RTL. start and end mean “left” for non-RTL and “right” for RTL.

TIP

The offset prop does not translate the popup by a number of pixels. It expands the anchor element’s bounding box outward: offset[0] moves that box’s left edge to the left and its right edge to the right, while offset[1] moves top up and bottom down. The popup’s self point is then aligned to the anchor point of the expanded box, and only after that is the result clamped to the available screen real estate.

Two consequences are worth knowing, because both make an offset look like it is being ignored on one axis:

  • A middle or center anchor point does not move with the offset. Expanding both edges by the same amount leaves the midpoint exactly where it was, so offset[0] is a no-op for anchor="... middle" and offset[1] is a no-op for anchor="center ...", no matter which value you pass. QMenu’s default anchor (bottom start) responds on both axes, but a middle/center one that you set yourself will not.
  • A clamped popup does not move with the offset either. Since the offset pushes the box outward, anchoring to a full-width or screen-edge element (or passing a very large value) can send the popup past a viewport edge, where it gets clamped back and the final position no longer depends on the offset value. Attach QMenu to an inline or inline-block trigger and point anchor/self into free space, so the offset has room to take effect.

The cover prop ignores offset altogether, since the popup is meant to sit right on top of the anchor element.


Fit
Cover
Hover
Anchor Origin
Vertical
Horizontal
Self Origin
Vertical
Horizontal

<q-menu hover anchor="bottom left" self="top left">
  <q-item clickable>
    <q-item-section>New tab</q-item-section>
  </q-item>
  <q-item clickable>
    <q-item-section>New incognito tab</q-item-section>
  </q-item>
</q-menu>

Accessibility
v2.25+

Semantics

QMenu renders as a plain positioned container, deliberately claiming no ARIA role of its own: it can host any kind of content, while the WAI-ARIA menu role permits nothing but menu items as children — a form, a date picker or a list would all become invalid markup under it.

So when the popup content really is a menu — a list of commands — declare it as such by setting role="menu" on the wrapping QList. The contained QItems then adapt automatically: actionable ones (clickable or link items, including disabled ones) expose themselves as menuitem, anything else (section headers etc.) stays neutral, and QSeparator already announces itself as a separator. An individual item can override its derived role — for instance with role="menuitemcheckbox" or role="menuitemradio" for toggle entries, in which case managing aria-checked is up to you. The “Basic” example above shows the declaration.

If you attach a role to the QMenu container itself instead (it forwards any role you pass), make sure its entire content satisfies that role’s requirements.

Anchor semantics

The anchor is your own markup, yet it is the control that opens the popup, so QMenu maintains the trigger’s ARIA on it from the outside: aria-expanded follows the open state, and a role declared on the QMenu container itself is mirrored onto the anchor as aria-haspopup (that attribute names the popup’s role, so only menu, listbox, tree, grid and dialog can be mirrored). A <q-btn> wrapping a QMenu is therefore announced as a collapsed or expanded trigger with no work on your part.

This requires an anchor that ARIA allows the state on — a <button>, a link with an href, or any element declaring a widget role such as role="button". A plain <div> computes to the generic role, where aria-expanded is invalid, so QMenu leaves it untouched: give such an anchor a proper role (and a keyboard path to activate it) before using it as a trigger. Two more cases are deliberately left alone — a context-menu popup, which opens on right click or long tap rather than on activation, and any aria-expanded/aria-haspopup you set on the anchor yourself, which QMenu never overwrites.

Do keep in mind that when the role lives on the QList inside the popup — the shape recommended above — QMenu cannot see it, so add aria-haspopup="menu" on the anchor yourself:

<q-btn label="Actions" aria-haspopup="menu">
  <q-menu>
    <q-list role="menu">
      <!-- ... -->
    </q-list>
  </q-menu>
</q-btn>

Keyboard navigation

Since the menu renders next to the end of the page, letting Tab walk past its last focusable element (or Shift + Tab before its first one) would drop keyboard focus out of the page. Following the WAI-ARIA APG, the menu instead closes and focus continues from its anchor, just like Escape closes it while returning focus to the anchor. Tabbing between multiple focusable elements inside the menu works as usual, and a persistent menu opts out of this dismissal too.

Note that focusable menu items are plain Tab stops — QMenu does not (yet) provide the Arrow-key navigation that the APG menu pattern describes for role="menu" content.