> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Menu - Web

> Menus provide the user with an actionable list of options.

> [!NOTE]
>
> For development, import `Menu` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=2648-41545).

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/menu-overview.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=d450225fadad56cfb3401a0bc915fcc8)

## Anatomy

The Menu consists of four primary elements that work together to provide users with an actionable list of options.

![Menu
anatomy](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/menu/design/menu-anatomy.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=51d1bf25691ecac804215f6618502f57)

1. Trigger
2. Popover
3. Menu item
4. Nested Menu item

## Options

The Menu supports flat and nested configurations to accommodate various action list scenarios.

### Flat

![Menu options
default](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/menu/shared/menu-options-default.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=a8d646827f4826866f4216f3eee65c26)

### Nested

![Menu options
nested](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/menu/design/menu-options-nested.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=ed09b2a2b269923c31cf54c6a29f7076)

## Behavior

The Menu responds to user interaction with distinct visual states and synchronized focus and hover behaviors.

### Visual States

#### Default

![Menu states
default](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/menu/shared/menu-options-default.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=a8d646827f4826866f4216f3eee65c26)

#### Hover

![Menu states
hover](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/menu/design/menu-states-hover.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=93700de09f2422574eedc3abda18e900)

#### Disabled

![Menu states
disabled](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/menu/design/menu-states-disabled.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=210d9290af6d303252544e5ab76f43aa)

### Focusing

Focus and hover states are synchronized. Moving the mouse over a menu item will automatically set focus to that item. Similarly, navigating through the menu with keyboard focus will trigger the hover state on the focused item.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoProvide multiple actions | Use the Menu to provide multiple actions to the user. |
| Don'tReplace a few Buttons that fit | Use individual Buttons when there are fewer than five actions and they fit in the available space. [Page Header](https://anvil.servicetitan.com/docs/components/page/web.md) is an exception to this, as it explicitly disallows this. |

### Alternatives

#### Menu vs Combobox

The Menu is used to perform actions on the page, and sometimes also a navigational context. The Combobox is used for choosing an option amongst a list, usually in the context of a Form.

#### Menu vs Popover

When a list of actions need to take place, the Menu should be used. It is not recommended to recreate menu-like interactions through a Popover.

### How to Use

#### Parent and Children

Menu trigger should have direct relationship to its children. This also applies to the nested version of the Menu.

![Menu parent children
example](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/menu/web/menu-parent-children-example.png)

![Nested Menu
example](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/menu/web/nested-menu-example.png)

Nested menu example

#### Disabled Handling

* When an action is temporarily unavailable, use disabled state
* When a user does not have the credentials to take action, do not display these unavailable actions to them. This avoids cluttering the UI and confusing the user when they are unable to take action. When a component is disabled, it is not interactive, meaning tooltips are not available to inform more.

#### Grouping

Menu does not allow for grouping. To group elements, use the Nested Menu.

## Content

Content within the Menu should clearly communicate each action through descriptive labels.

For content guidance, refer to the content guidance provided in the [Button](https://anvil.servicetitan.com/docs/components/button/design).

## Keyboard Interaction

Users can navigate the Menu using standard keyboard controls.

### Trigger

| Key | Interaction |
| - | - |
| Enter/Space | Opens the menu and places focus on the first menu item. |
| Arrow Down | Opens the menu and moves focus to the first menu item. |
| Arrow up | Opens the menu and moves focus to the last menu item. |

### Menu Item

| Key | Interaction |
| - | - |
| Arrow Down | Focuses next menu item. |
| Arrow Up | Focuses previous menu item. |
| Arrow Right | If nested, opens the nested menu and places focus on the first nested menu item. |
| Arrow Left | If focus is inside nested menu, closes the nested menu and places focus on the root menu item. |

## Code

**Implementation**

[menu-playground](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/menu-playground?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

## Common Examples

```tsx theme={null}
import { Menu } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Menu label="Click me!">
      <Menu.Item label="Item 1" />
      <Menu.Item label="Item 2" />
      <Menu.Item label="Item 3" />
    </Menu>
  );
}
```

### Creating simple dropdown menus

The `Menu` and `Menu.Item` components can be used to compose basic dropdown menus.

[menu-item](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/menu-item?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

### Nesting menus in dropdowns

To create nested dropdown menus, simply add a `Menu` as a child of another `Menu`.

[menu-menu](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/menu-menu?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

### Creating custom menu triggers

To use a custom trigger element, use the `trigger` prop of the `Menu`, and omit the `label` prop (and any other prop meant to be passed to the default [Button](https://anvil.servicetitan.com/docs/components/button/react-native.md) trigger).

[menu-trigger](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/menu-trigger?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

### Creating custom nested menu triggers

Nested menus support the `trigger` prop the same way root menus do. Spread all props onto your trigger element, including `ref`, `className`, and `data-anv`, so the nested item receives menu styling and participates in keyboard navigation. A custom `trigger` replaces the default nested button, including the submenu chevron — include that icon in your markup.

[menu-nested-trigger](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/menu-nested-trigger?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

## React Accessibility

When using a custom trigger, make sure that the trigger:

* Can receive focus via keyboard when tabbing.
* Opens the menu when the user presses enter.
* Closes the menu and returns focus to the trigger when the user presses escape.

Nested custom triggers also need:

* `role`, `data-anv`, and `tabIndex` spread onto the element so the parent menu's focus query includes them.
* ArrowRight to open the submenu.
* ArrowLeft to close the submenu and return focus to the nested trigger.

For more guidance on accessible menu interactions, see [keyboard navigation and focus management best practices](https://anvil.servicetitan.com/docs/resources/contributing/accessible-custom-components.md).

**Default Menu Props**

```tsx theme={null}
<Menu
  label="Click me!"
  open={false}
  disableAutoHeight={false}
  appearance="secondary"
  size="medium"
>
  <Menu.Item label="Item 1" />
  <Menu.Item label="Item 2" />
</Menu>
```

## Default `Menu` Props

Use the `trigger` prop to determine what element should open the menu. If `trigger` is omitted, a `Button` is used as the trigger by default. In this case, all props available to the [`Button` component](https://anvil.servicetitan.com/docs/components/button/design) can be used on the `Menu`. In addition, the following props are available:

**label** `string`

Alternatively, `aria-label` or `aria-labelledby` can be used.

**disableAutoHeight** `boolean`

This will stop the menu from automatically deciding which way to open based on
the trigger's location on the screen.

**open** `boolean`

**Custom Trigger Props**

```tsx theme={null}
<Menu
  trigger={(props) => <CustomButton {...props}>Custom Trigger</CustomButton>}
  open={false}
  disableAutoHeight={false}
>
  <Menu.Item label="Item 1" />
  <Menu.Item label="Item 2" />
</Menu>
```

## Custom Trigger `Menu` Props

When using the `trigger` prop to render a custom trigger, the following props are available:

**disableAutoHeight** `boolean`

This will stop the menu from automatically deciding which way to open based on
the trigger's location on the screen.

**open** `boolean`

**trigger** `(TriggerReturnProps) =\> ReactElement`

**TriggerReturnProps**

```tsx theme={null}
<Menu
  trigger={({ ref, ...rest }) => (
    <button type="button" {...rest} ref={ref}>
      Custom Trigger
    </button>
  )}
>
  <Menu.Item label="Item 1" />
</Menu>
```

## `TriggerReturnProps`

The following props are passed as an object parameter to the `trigger` callback, on both root and nested menus. The function returns a ReactElement, and every prop must be spread onto that element (see [Creating custom menu triggers](https://anvil.servicetitan.com/docs/components/menu/code#creating-custom-menu-triggers)).

> [!WARNING]
>
> Each prop below is optional, so omitting one still compiles. On a nested menu,
> `role`, `data-anv`, and `tabIndex` are what the parent menu's focus query
> matches on — a trigger missing them is skipped by keyboard navigation.

**data-state** `"open" | "close"` *required*

Whether the menu the trigger controls is currently open.

**aria-controls** `string`

Identifies the element (or elements) whose contents or presence are controlled
by the current element.

**aria-expanded** `boolean | "true" | "false"`

Indicates whether the element, or another grouping element it controls, is
currently expanded or collapsed.

**aria-haspopup** `boolean | "dialog" | "grid" | "listbox" | "menu" | "tree" | "true" | "false"`

Indicates the availability and type of interactive popup element, such as menu
or dialog, that can be triggered by an element.

**className** `string`

Applies the menu item styling. Nested menus only.

**data-anv** `"menu-item"`

Marks the trigger as a focusable menu item. Nested menus only.

**data-open** `boolean`

**id** `string`

**onBlur** `(e: FocusEvent\<HTMLElement\>) =\> void`

**onClick** `() =\> void`

**onFocus** `(e: FocusEvent\<HTMLElement\>) =\> void`

**onKeyDown** `(e: KeyboardEvent\<HTMLElement\>) =\> void`

**onMouseEnter** `(e: MouseEvent\<HTMLElement\>) =\> void`

**ref** `Ref\<HTMLButtonElement\>`

**role** `"menuitem"`

Nested menus only.

**tabIndex** `number`

Roving tab index — `0` while the item is focused, `-1` otherwise. Nested menus
only.

**Menu.Item Props**

```tsx theme={null}
<Menu.Item
  label="Menu Item"
  disabled={false}
  icon={EditIcon}
  onClick={() => console.log("Clicked")}
/>
```

## `Menu.Item` Props

In addition to the props listed below, the `Menu.Item` component can accept any valid HTML `button` props.

**label** `ReactNode` *required*

**disabled** `boolean` (default: false)

**icon** `Svg`

The SVGR library imports `*.svg` files as React components, so `icon` accepts
a React component.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.