Menu
A short list of actions that opens from a button.
SourcePackage @weft-js/warp
Preview
tsx
import { Menu } from '@weft-js/warp'export function Basic() { return ( <Menu label="Actions" items={[ { label: 'Rename', icon: 'pencil', shortcut: '⌘R' }, { label: 'Duplicate', icon: 'copy', shortcut: '⌘D' }, { separator: true }, { label: 'Delete', icon: 'trash', tone: 'critical' }, ]} /> )}Installation
npm install @weft-js/warpUsage
tsx
import { Menu } from '@weft-js/warp'<Menu label="Actions" items={[ { label: 'Rename', icon: 'pencil', shortcut: '⌘R' }, { label: 'Duplicate', icon: 'copy', shortcut: '⌘D' }, { separator: true }, { label: 'Delete', icon: 'trash', tone: 'critical' },]} />Placement
Use side to pick where it opens.
tsx
import { Menu } from '@weft-js/warp'export function Placement() { return ( <> {(['top', 'bottom', 'left', 'right'] as const).map(side => ( <Menu key={side} label={side} side={side} trigger={{ emphasis: 'outline', tone: 'neutral' }} items={[{ label: 'Rename' }, { label: 'Duplicate' }, { label: 'Archive' }]} /> ))} </> )}Alignment
Use align to line it up with the button.
tsx
import { Menu } from '@weft-js/warp'export function Alignment() { return ( <> {(['start', 'center', 'end'] as const).map(align => ( <Menu key={align} label={align} align={align} trigger={{ emphasis: 'outline', tone: 'neutral' }} items={[{ label: 'Rename' }, { label: 'Duplicate' }, { label: 'Archive' }]} /> ))} </> )}Groups
A group entry names what follows.
tsx
import { Menu } from '@weft-js/warp'export function Groups() { return ( <Menu label="Deploy to" items={[ { group: 'Production' }, { label: 'eu-west' }, { label: 'us-east' }, { group: 'Preview' }, { label: 'staging' }, ]} /> )}Trigger styles
trigger takes any Button prop.
tsx
import { Menu } from '@weft-js/warp'export function Trigger() { return ( <> <Menu label="Sort" trigger={{ emphasis: 'ghost', tone: 'neutral' }} items={[{ label: 'Newest' }, { label: 'Oldest' }]} /> <Menu label="Export" trigger={{ emphasis: 'soft' }} items={[{ label: 'CSV' }, { label: 'JSON' }]} /> <Menu label="More" trigger={{ emphasis: 'outline', tone: 'neutral', size: 'sm' }} items={[{ label: 'Archive' }]} /> </> )}A row's actions
Use items to list a row's actions, and trigger to make the button quiet enough to sit in a row.
tsx
import { Menu, Row, Stack, Text } from '@weft-js/warp'export function RowActions() { return ( <Row justify="between" gap="group"> <Stack gap="hair"> <Text role="heading">checkout</Text> <Text subtle>Live in eu-west</Text> </Stack> <Menu label="Manage" align="end" trigger={{ emphasis: 'ghost', tone: 'neutral', size: 'sm' }} items={[ { label: 'Open logs', icon: 'external' }, { label: 'Redeploy', icon: 'rocket' }, { separator: true }, { label: 'Pause', tone: 'critical' }, ]} /> </Row> )}When to use
Use it for
- Several actions on one thing, and not much room
Reach for something else
- Select, when people are choosing a value
- CommandPalette, when there are hundreds of commands
Accessibility
- Enter
- Opens, or picks the highlighted item
- ↑↓
- Moves through the items
- HomeEnd
- Jumps to the first or last
- Esc
- Closes and returns focus
- Aletter
- Jumps to the next item starting with it
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | unknown | — | The trigger's text. |
items | MenuEntry[] | — | The entries: an action, { separator: true } or { group }. |
trigger | ButtonProps | — | Button props for the trigger. |
side | Side | 'bottom' | Which side it opens on. |
align | Align | 'start' | How it lines up with the trigger. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="menu"] [data-part="content"]
contentshuttleseparatorgroup-labelitem
RelatedButtonPopoverCommand palette