# @spectrum-web-components/menu

> An `` is used for creating a menu list. The various elements inside a menu are given as [``](../menu-group), [``](../menu-item), or ``. Often a `` element will appear in a [``](..

Latest version **1.12.5** (published 2026-10-14) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install @spectrum-web-components/menu
pnpm add @spectrum-web-components/menu
yarn add @spectrum-web-components/menu
bun add @spectrum-web-components/menu
```

## Health

**Score 55/100 (C)** — status: active.

Positive: no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; no esm support.

## Facts

| | |
|---|---|
| Version | 1.12.5 |
| Published | 2026-10-14 |
| First published | 2019-09-17 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1576 |
| Author | Adobe |
| Maintainers | caseyisonit, taratadobe, rajrock38, jnjosh, nikkimk, rubenc, jianliao79, pvashish, patrickfulton |
| Keywords | design-system, spectrum, adobe, adobe-spectrum, web components, web-components, lit-element, lit-html, component, css |

## Links

- npm: https://www.npmjs.com/package/@spectrum-web-components/menu
- Repository: https://github.com/adobe/spectrum-web-components
- Homepage: https://opensource.adobe.com/spectrum-web-components/components/menu
- Issues: https://github.com/adobe/spectrum-web-components/issues
- npm.io page: https://npm.io/package/@spectrum-web-components/menu

## Alternatives

- [style-dictionary](https://npm.io/package/style-dictionary.md) — 2.0M weekly downloads
- [postcss-merge-idents](https://npm.io/package/postcss-merge-idents.md) — 1.7M weekly downloads
- [@fontsource/noto-sans](https://npm.io/package/@fontsource/noto-sans.md) — 93.0K weekly downloads
- [uglifycss](https://npm.io/package/uglifycss.md) — 71.6K weekly downloads
- [mat4-interpolate](https://npm.io/package/mat4-interpolate.md) — 23.3K weekly downloads

## Recent versions

- 1.12.5 (latest) — 2026-10-14
- 1.12.3-next.20260903033550 (next) — 2026-09-03
- 1.12.3-snapshot-test.20260831180357 (snapshot-test) — 2026-08-31
- 1.12.3-test.20260827122947 (test) — 2026-08-27
- 1.12.0-snapshot.20260422090428 (snapshot) — 2026-04-22
- 1.12.0-snaphot-test.20260223152537 (snaphot-test) — 2026-02-23
- 1.12.0-testing.20260223092154 (testing) — 2026-02-23
- 1.11.0-preview-ae61361d.20251203202755 (preview) — 2025-12-03
- 1.9.1-nightly.20251101120150 (nightly) — 2025-11-01
- 1.6.1-beta.0 (beta) — 2025-05-05
- 0.0.0-20250410163257 (calendar) — 2025-04-10
- 1.0.1-color-testing (color-testing) — 2024-12-09
- 1.0.0-rc.10 (rc) — 2024-10-30
- 0.16.10-style.28 (style) — 2023-01-04
- 0.16.10-styles.28 (styles) — 2023-01-04
- … 473 more at https://npm.io/package/@spectrum-web-components/menu/versions

## README

## Overview

An `<sp-menu>` is used for creating a menu list. The various elements inside a menu are given as [`<sp-menu-group>`](../menu-group), [`<sp-menu-item>`](../menu-item), or `<sp-menu-divider>`. Often a `<sp-menu>` element will appear in a [`<sp-popover>`](../popover) element so that it displays as a toggling menu.

### Usage

[![See it on NPM!](https://img.shields.io/npm/v/@spectrum-web-components/menu?style=for-the-badge)](https://www.npmjs.com/package/@spectrum-web-components/menu)
[![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/@spectrum-web-components/menu?style=for-the-badge)](https://bundlephobia.com/result?p=@spectrum-web-components/menu)
[![Try it on Stackblitz](https://img.shields.io/badge/Try%20it%20on-Stackblitz-blue?style=for-the-badge)](https://stackblitz.com/edit/vitejs-vite-ascdqv3p)

```
yarn add @spectrum-web-components/menu
```

Import the side effectful registration of `<sp-menu>`, `<sp-menu-group>`, `<sp-menu-item>`, or `<sp-menu-divider>` individually as follows:

```
import '@spectrum-web-components/menu/sp-menu.js';
import '@spectrum-web-components/menu/sp-menu-group.js';
import '@spectrum-web-components/menu/sp-menu-item.js';
import '@spectrum-web-components/menu/sp-menu-divider.js';
```

When looking to leverage the `Menu`, `MenuGroup`, `MenuItem`, or `MenuDivider` base classes as a type and/or for extension purposes, do so via:

```
import {
    Menu,
    MenuGroup,
    MenuItem,
    MenuDivider
} from '@spectrum-web-components/menu';
```

### Anatomy

<!-- prettier-ignore -->
```html
<sp-menu label="Selection type">
    <sp-menu-item>
        Deselect
    </sp-menu-item>
    <sp-menu-item>
        Select inverse
    </sp-menu-item>
    <sp-menu-item>
        Feather...
    </sp-menu-item>
    <sp-menu-item>
        Select and mask...
    </sp-menu-item>
    <sp-menu-item>
        Save selection
    </sp-menu-item>
    <sp-menu-item disabled>
        Make work path
    </sp-menu-item>
</sp-menu>
```

#### Popover menus

Often an `<sp-menu>` element will be delivered inside of an `<sp-popover>` element when displaying it above other content.

```html
<sp-popover open style="position: relative" label="Selection type">
  <sp-menu>
    <sp-menu-item value="item-1">Deselect</sp-menu-item>
    <sp-menu-item value="item-2">Select inverse</sp-menu-item>
    <sp-menu-item value="item-3">Feather...</sp-menu-item>
    <sp-menu-item value="item-4">Select and mask...</sp-menu-item>
    <sp-menu-item value="item-5">Save selection</sp-menu-item>
    <sp-menu-item value="item-6" disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

#### Labels

To render accessibly, an `<sp-menu>` element or its parent `<sp-popover>` must have a label. For an accessible label that is visibly hidden, but can still be read by assistive technology, use the `label` attribute.

<sp-tabs selected="sp-field-label" auto label="Label options">
<sp-tab value="sp-field-label">Menu with label</sp-tab>
<sp-tab-panel value="sp-field-label">

```html demo
<sp-menu id="menu-label-attribute" label="Selection type">
  <sp-menu-item>Deselect</sp-menu-item>
  <sp-menu-item>Select inverse</sp-menu-item>
  <sp-menu-item>Feather...</sp-menu-item>
  <sp-menu-item>Select and mask...</sp-menu-item>
  <sp-menu-divider></sp-menu-divider>
  <sp-menu-item>Save selection</sp-menu-item>
  <sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
```

</sp-tab-panel>
<sp-tab value="label-attribute">Popover with label</sp-tab>
<sp-tab-panel value="label-attribute">

```html demo
<sp-popover open style="position: relative" label="Selection type:">
  <sp-menu id="popover-label-attribute">
    <sp-menu-item>Deselect</sp-menu-item>
    <sp-menu-item>Select inverse</sp-menu-item>
    <sp-menu-item>Feather...</sp-menu-item>
    <sp-menu-item>Select and mask...</sp-menu-item>
    <sp-menu-divider></sp-menu-divider>
    <sp-menu-item>Save selection</sp-menu-item>
    <sp-menu-item disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

</sp-tab-panel>
</sp-tabs>

### Options

#### Sizes

<sp-tabs selected="m" auto label="Size attribute options">
<sp-tab value="s">Small</sp-tab>
<sp-tab-panel value="s">

```html demo
<sp-menu id="menu-s" size="s" label="Selection type">
  <sp-menu-item>Deselect</sp-menu-item>
  <sp-menu-item>Select inverse</sp-menu-item>
  <sp-menu-item>Feather...</sp-menu-item>
  <sp-menu-item>Select and mask...</sp-menu-item>
  <sp-menu-divider></sp-menu-divider>
  <sp-menu-item>Save selection</sp-menu-item>
  <sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-popover open style="position: relative" label="Selection type:">
  <sp-menu id="menu-s-popover" size="s">
    <sp-menu-item>Deselect</sp-menu-item>
    <sp-menu-item>Select inverse</sp-menu-item>
    <sp-menu-item>Feather...</sp-menu-item>
    <sp-menu-item>Select and mask...</sp-menu-item>
    <sp-menu-divider></sp-menu-divider>
    <sp-menu-item>Save selection</sp-menu-item>
    <sp-menu-item disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

</sp-tab-panel>
<sp-tab value="m">Medium</sp-tab>
<sp-tab-panel value="m">

```html demo
<sp-menu id="menu-m" size="m" label="Selection type">
  <sp-menu-item>Deselect</sp-menu-item>
  <sp-menu-item>Select inverse</sp-menu-item>
  <sp-menu-item>Feather...</sp-menu-item>
  <sp-menu-item>Select and mask...</sp-menu-item>
  <sp-menu-divider></sp-menu-divider>
  <sp-menu-item>Save selection</sp-menu-item>
  <sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-popover open style="position: relative" label="Selection type:">
  <sp-menu id="menu-m-popover" size="m">
    <sp-menu-item>Deselect</sp-menu-item>
    <sp-menu-item>Select inverse</sp-menu-item>
    <sp-menu-item>Feather...</sp-menu-item>
    <sp-menu-item>Select and mask...</sp-menu-item>
    <sp-menu-divider></sp-menu-divider>
    <sp-menu-item>Save selection</sp-menu-item>
    <sp-menu-item disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

</sp-tab-panel>
<sp-tab value="l">Large</sp-tab>
<sp-tab-panel value="l">

```html demo
<sp-menu id="menu-l" size="l" label="Selection type">
  <sp-menu-item>Deselect</sp-menu-item>
  <sp-menu-item>Select inverse</sp-menu-item>
  <sp-menu-item>Feather...</sp-menu-item>
  <sp-menu-item>Select and mask...</sp-menu-item>
  <sp-menu-divider></sp-menu-divider>
  <sp-menu-item>Save selection</sp-menu-item>
  <sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-popover open style="position: relative" label="Selection type:">
  <sp-menu id="menu-l-popover" size="l">
    <sp-menu-item>Deselect</sp-menu-item>
    <sp-menu-item>Select inverse</sp-menu-item>
    <sp-menu-item>Feather...</sp-menu-item>
    <sp-menu-item>Select and mask...</sp-menu-item>
    <sp-menu-divider></sp-menu-divider>
    <sp-menu-item>Save selection</sp-menu-item>
    <sp-menu-item disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

</sp-tab-panel>
<sp-tab value="xl">Extra Large</sp-tab>
<sp-tab-panel value="xl">

```html demo
<sp-menu id="menu-xl" size="xl" label="Selection type">
  <sp-menu-item>Deselect</sp-menu-item>
  <sp-menu-item>Select inverse</sp-menu-item>
  <sp-menu-item>Feather...</sp-menu-item>
  <sp-menu-item>Select and mask...</sp-menu-item>
  <sp-menu-divider></sp-menu-divider>
  <sp-menu-item>Save selection</sp-menu-item>
  <sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-popover open style="position: relative" label="Selection type:">
  <sp-menu id="menu-xl-popover" size="xl">
    <sp-menu-item>Deselect</sp-menu-item>
    <sp-menu-item>Select inverse</sp-menu-item>
    <sp-menu-item>Feather...</sp-menu-item>
    <sp-menu-item>Select and mask...</sp-menu-item>
    <sp-menu-divider></sp-menu-divider>
    <sp-menu-item>Save selection</sp-menu-item>
    <sp-menu-item disabled>Make work path</sp-menu-item>
  </sp-menu>
</sp-popover>
```

</sp-tab-panel>
</sp-tabs>

#### Selection

The `<sp-menu>` element can be instructed to maintain a selection via the `selects` attribute. Depending on the chosen algorithm, the `<sp-menu>` element will hold a `value` property and manage the `selected` state of its `<sp-menu-item>` descendants.

- When `selects="single"`, the `<sp-menu>` element will maintain one selected item after an initial selection is made.
- When `selects` is set to `multiple`, the `<sp-menu>` element will maintain zero or more selected items.
- When `selects` is set to `inherit`, the `<sp-menu>` element will allow its `<sp-menu-item>` children to participate in the selection of its nearest `<sp-menu>` ancestor.

<sp-tabs selected="selects-single" auto label="Selects attribute">
<sp-tab value="selects-single">Single</sp-tab>
<sp-tab-panel value="selects-single">

```html demo
<p>
  The value of the `&lt;sp-menu&gt;` element is:
  <span id="single-value"></span>
</p>
<sp-menu
  label="Choose a shape"
  selects="single"
  onchange="this.previousElementSibling.querySelector('#single-value').textContent=this.value"
>
  <sp-menu-item value="item-1">Square</sp-menu-item>
  <sp-menu-item value="item-2" selected>Triangle</sp-menu-item>
  <sp-menu-item value="item-3">Parallelogram</sp-menu-item>
  <sp-menu-item value="item-4">Star</sp-menu-item>
  <sp-menu-item value="item-5">Hexagon</sp-menu-item>
  <sp-menu-item value="item-6" disabled>Circle</sp-menu-item>
</sp-menu>
```

</sp-tab-panel>
<sp-tab value="selects-multiple">Multiple</sp-tab>
<sp-tab-panel value="selects-multiple">

```html demo
<p>
  The value of the `&lt;sp-menu&gt;` element is:
  <span id="multiple-value">item-3,item-4</span>
</p>
<sp-menu
  label="Choose some fruit"
  selects="multiple"
  onchange="this.previousElementSibling.querySelector('#multiple-value').textContent=this.value"
>
  <sp-menu-item value="item-1">Apple</sp-menu-item>
  <sp-menu-item value="item-2">Banana</sp-menu-item>
  <sp-menu-item value="item-3" selected>Goji berry</sp-menu-item>
  <sp-menu-item value="item-4" selected>Grapes</sp-menu-item>
  <sp-menu-item value="item-5" disabled>Kumquat</sp-menu-item>
  <sp-menu-item value="item-6">Orange</sp-menu-item>
</sp-menu>
```

</sp-tab-panel>
<sp-tab value="selects-inherit">Inherit</sp-tab>
<sp-tab-panel value="selects-inherit">

```html demo
<p>
  The value of the `&lt;sp-menu&gt;` element is:
  <span id="inherit-value">item-3 || item-4 || item-8 || item-11</span>
</p>
<sp-menu
  label="Choose some groceries"
  selects="multiple"
  value-separator=" || "
  onchange="this.previousElementSibling.querySelector('#inherit-value').textContent=this.value"
>
  <sp-menu label="Fruit" selects="inherit">
    <sp-menu-item value="item-1">Apple</sp-menu-item>
    <sp-menu-item value="item-2">Banana</sp-menu-item>
    <sp-menu-item value="item-3" selected>Goji berry</sp-menu-item>
    <sp-menu-item value="item-4" selected>Grapes</sp-menu-item>
    <sp-menu-item value="item-5" disabled>Kumquat</sp-menu-item>
    <sp-menu-item value="item-6">Orange</sp-menu-item>
  </sp-menu>
  <sp-menu label="Vegetables" selects="inherit">
    <sp-menu-item value="item-7">Carrot</sp-menu-item>
    <sp-menu-item value="item-8" selected>Garlic</sp-menu-item>
    <sp-menu-item value="item-9" disabled>Lettuce</sp-menu-item>
    <sp-menu-item value="item-10">Onion</sp-menu-item>
    <sp-menu-item value="item-11" selected>Potato</sp-menu-item>
    <sp-menu-item value="item-12">Tomato</sp-menu-item>
  </sp-menu>
</sp-menu>
```

</sp-tab-panel>
</sp-tabs>

### Behaviors

#### Mobile view (drill-down navigation)

On small screens, flyout submenus are difficult to use because the overlay extends outside the tray. The `mobile-view` attribute switches an `<sp-menu>` to a drill-down navigation mode: activating a menu item that has a submenu slides the submenu's items in to replace the current content, with a back button to return to the previous level. This pattern works well inside an `<sp-tray>`.

```html demo
<overlay-trigger type="modal">
  <sp-button slot="trigger" variant="secondary">Open menu</sp-button>
  <sp-tray slot="click-content">
    <sp-menu mobile-view label="Options">
      <sp-menu-item>Home</sp-menu-item>
      <sp-menu-item>
        File
        <sp-menu slot="submenu">
          <sp-menu-item>New</sp-menu-item>
          <sp-menu-item>Open</sp-menu-item>
          <sp-menu-item>Save</sp-menu-item>
        </sp-menu>
      </sp-menu-item>
      <sp-menu-item>Settings</sp-menu-item>
    </sp-menu>
  </sp-tray>
</overlay-trigger>
```

The back button label defaults to "Back". Use the `mobile-back-label` attribute to provide a localized string:

```html
<sp-menu mobile-view mobile-back-label="Retour" label="Options">
  <!-- ... -->
</sp-menu>
```

The slide animation respects the user's `prefers-reduced-motion` setting, and the back-arrow icon mirrors automatically in RTL layouts.

The `<sp-menu mobile-view>` does not render its own tray; compose it inside an `<sp-tray>` (typically via `<overlay-trigger>`) as shown above. When that tray is dismissed while drilled into a submenu, the drill-down resets so reopening returns to the top-level menu.

When using `<sp-action-menu>`, `mobile-view` is applied automatically on mobile devices. To always render a popover instead of a tray (and disable `mobile-view`), add the `force-popover` attribute to `<sp-action-menu>`.

#### "change" event

Regardless of whether or not `<sp-menu>` carries a selection, when one of the `<sp-menu-item>` children that it manages is activated, the `<sp-menu>` element will dispatch a `change` event. At dispatch time, even when a selection is not held due to the absence of the `selects` attribute, the `value` of the `<sp-menu>` will correspond to the `<sp-menu-item>` that was activated. When the `selects` attribute is present, this `value` will persist beyond the lifecycle of the `change` event. When `selects="multiple"`, the values of multiple items will be comma separated, unless otherwise set via the `value-separator` attribute.

Note: The `change` event is only dispatched on a left mouse click or Enter/Space keypress. Right/Middle mouse clicks will not dispatch the `change` event.

### Accessibility

Review the accessibility guidelines for the [menu-item](../menu-item#accessibility-guidelines) and [menu-group](../menu-group#accessibility-guidelines) descendants.

#### Include a label

A menu is required to have an accessible label.

---
_Source: https://npm.io/package/@spectrum-web-components/menu · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
