Skip to main content
View Zag.js on Github
Join the Discord server

Context Menu

An accessible dropdown and context menu that is used to display a list of actions or options that a user can choose when a trigger element is right-clicked or long pressed.

Features

  • Support for items, labels, groups of items
  • Focus is fully managed using aria-activedescendant pattern
  • Typeahead to allow focusing items by typing text
  • Keyboard navigation support including arrow keys, home/end, page up/down

Installation

To use the menu machine in your project, run the following command in your command line:

npm install @zag-js/menu @zag-js/vue # or yarn add @zag-js/menu @zag-js/vue

Anatomy

To set up the menu correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

First, import the menu package into your project

import * as menu from "@zag-js/menu"

The menu package exports two key functions:

  • machine — The state machine logic for the menu widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the menu machine in your project 🔥

To show the menu when a trigger element is right-clicked, use the contextTriggerProps provided by the menu's connect function.

Context menu's are also opened during a long-press of roughly 700ms when the pointer is pen or touch.

<script setup> import * as menu from "@zag-js/menu" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(menu.machine, { id: "1", "aria-label": "File" }) const api = computed(() => menu.connect(service, normalizeProps)) </script> <template> <div ref="ref"> <button v-bind="api.getContextTriggerProps()"> <div :style="{ border: 'solid 1px red' }">Open context menu</div> </button> <div v-bind="api.getPositionerProps()"> <ul v-bind="api.getContentProps()"> <li v-bind="api.getItemProps({ value: 'edit' })">Edit</li> <li v-bind="api.getItemProps({ value: 'duplicate' })">Duplicate</li> <li v-bind="api.getItemProps({ value: 'delete' })">Delete</li> <li v-bind="api.getItemProps({ value: 'export' })">Export...</li> </ul> </div> </div> </template>

Styling guide

Earlier, we mentioned that each menu part has a data-part attribute added to them to select and style them in the DOM.

Focused item state

When an item is focused, via keyboard navigation or pointer, it is given a data-focus attribute.

[data-part="item"][data-focus] { /* styles for focused state */ } [data-part="item"][data-type="radio|checkbox"][data-focus] { /* styles for focused state */ }

Disabled item state

When an item or an option item is disabled, it is given a data-disabled attribute.

[data-part="item"][data-disabled] { /* styles for disabled state */ } [data-part="item"][data-type="radio|checkbox"][data-disabled] { /* styles for disabled state */ }

Using arrows

When using arrows within the menu, you can style it using css variables.

[data-part="arrow"] { --arrow-size: 20px; --arrow-background: red; }

Checked option item state

When an option item is checked, it is given a data-checked attribute.

[data-part="item"][data-type="radio|checkbox"][data-checked] { /* styles for checked state */ }

Methods and Properties

Machine Context

The menu machine exposes the following context properties:

  • idsPartial<{ trigger: string; contextTrigger: string; content: string; groupLabel(id: string): string; group(id: string): string; positioner: string; arrow: string; }>The ids of the elements in the menu. Useful for composition.
  • defaultHighlightedValuestringThe initial highlighted value of the menu item when rendered. Use when you don't need to control the highlighted value of the menu item.
  • highlightedValuestringThe controlled highlighted value of the menu item.
  • onHighlightChange(details: HighlightChangeDetails) => voidFunction called when the highlighted menu item changes.
  • onSelect(details: SelectionDetails) => voidFunction called when a menu item is selected.
  • anchorPointPointThe positioning point for the menu. Can be set by the context menu trigger or the button trigger.
  • loopFocusbooleanWhether to loop the keyboard navigation.
  • positioningPositioningOptionsThe options used to dynamically position the menu
  • closeOnSelectbooleanWhether to close the menu when an option is selected
  • aria-labelstringThe accessibility label for the menu
  • openbooleanThe controlled open state of the menu
  • onOpenChange(details: OpenChangeDetails) => voidFunction called when the menu opens or closes
  • defaultOpenbooleanThe initial open state of the menu when rendered. Use when you don't need to control the open state of the menu.
  • typeaheadbooleanWhether the pressing printable characters should trigger typeahead navigation
  • compositebooleanWhether the menu is a composed with other composite widgets like a combobox or tabs
  • navigate(details: NavigateDetails) => voidFunction to navigate to the selected item if it's an anchor element
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
  • onEscapeKeyDown(event: KeyboardEvent) => voidFunction called when the escape key is pressed
  • onPointerDownOutside(event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component
  • onFocusOutside(event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component
  • onInteractOutside(event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component

Machine API

The menu api exposes the following methods:

  • openbooleanWhether the menu is open
  • setOpen(open: boolean) => voidFunction to open or close the menu
  • highlightedValuestringThe id of the currently highlighted menuitem
  • setHighlightedValue(value: string) => voidFunction to set the highlighted menuitem
  • setParent(parent: MenuService) => voidFunction to register a parent menu. This is used for submenus
  • setChild(child: MenuService) => voidFunction to register a child menu. This is used for submenus
  • reposition(options?: Partial<PositioningOptions>) => voidFunction to reposition the popover
  • getOptionItemState(props: OptionItemProps) => OptionItemStateReturns the state of the option item
  • getItemState(props: ItemProps) => ItemStateReturns the state of the menu item
  • addItemListener(props: ItemListenerProps) => VoidFunctionSetup the custom event listener for item selection event

Data Attributes

Trigger
data-scope
menu
data-part
trigger
data-placement
The placement of the trigger
data-state
"open" | "closed"
Indicator
data-scope
menu
data-part
indicator
data-state
"open" | "closed"
Content
data-scope
menu
data-part
content
data-state
"open" | "closed"
data-placement
The placement of the content
Item
data-scope
menu
data-part
item
data-disabled
Present when disabled
data-highlighted
Present when highlighted
data-value
The value of the item
data-valuetext
The human-readable value
OptionItem
data-scope
menu
data-part
option-item
data-type
The type of the item
data-value
The value of the item
data-state
"checked" | "unchecked"
data-disabled
Present when disabled
data-highlighted
Present when highlighted
data-valuetext
The human-readable value
ItemIndicator
data-scope
menu
data-part
item-indicator
data-disabled
Present when disabled
data-highlighted
Present when highlighted
data-state
"checked" | "unchecked"
ItemText
data-scope
menu
data-part
item-text
data-disabled
Present when disabled
data-highlighted
Present when highlighted
data-state
"checked" | "unchecked"

Accessibility

Uses aria-activedescendant pattern to manage focus movement among menu items.

Keyboard Interactions

  • Space
    Activates/Selects the highlighted item
  • Enter
    Activates/Selects the highlighted item
  • ArrowDown
    Highlights the next item in the menu
  • ArrowUp
    Highlights the previous item in the menu
  • ArrowRightArrowLeft
    When focus is on trigger, opens or closes the submenu depending on reading direction.
  • Esc
    Closes the context menu

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2025
On this page