Pre-alphaThe registry and the CLI are not published yet.Roadmap

ContextMenu

A menu opened by a long press on content, with the content lifted above the backdrop, iOS style.

A long press on a message, a photo or a row opens a menu of actions for that item. The pressed content is lifted above the dimmed screen, so the user sees what the actions apply to.

ContextMenu is Menu with another trigger: Root, Content, Item, Group, Separator and Sub are the same parts.

Installation

npx axiom add context-menu
pnpm dlx axiom add context-menu
yarn dlx axiom add context-menu
bun x axiom add context-menu

Also copies: menu and its dependencies. context-menu.tsx holds only the trigger: it has no styles, so it’s the same file for every styling variant.

Usage

import { ContextMenu } from "@/components/ui/context-menu";

<ContextMenu.Root>
	<ContextMenu.Trigger>
		<PhotoThumbnail />
	</ContextMenu.Trigger>
	<ContextMenu.Content>
		<ContextMenu.Item icon="share" onPress={share}>
			Share
		</ContextMenu.Item>
		<ContextMenu.Separator />
		<ContextMenu.Item icon="trash" destructive onPress={remove}>
			Delete
		</ContextMenu.Item>
	</ContextMenu.Content>
</ContextMenu.Root>;

Props

ContextMenu.Trigger

PropTypeDefaultDescription
previewboolean | ReactNodetrueLifts a copy of the pressed element above the backdrop. Pass a node to show it in the element’s place instead.
hapticHapticKind | false'medium'Haptic played when the menu opens. false turns it off.
asChildbooleanfalseUses the child as the trigger.
accessibilityLabelstring—Label of the trigger, when its content has no text.
childrenReactNode—The element that opens the menu.

The other parts and their props are on the Menu page.

Haptics

The menu plays a medium haptic as it opens: it confirms the press was long enough, the way iOS context menus do. Set haptic to change the kind, or false to turn it off. Nothing plays until your app connects a haptics library: see Haptics.

Accessibility

The trigger announces “Long press for options”: screen readers reach the menu through the long press action. Keep the same actions reachable another way too, such as a “more” button or swipe actions.

Use cases

Long press on a message

The message is lifted as a preview above the backdrop; quick reactions sit above it.

Found the bug.

❤️👍😂😮

It was a moth. Taped it in the logbook.

Reply

Copy

Delete

<ContextMenu.Root>
	<ContextMenu.Trigger>
		<MessageBubble message={message} />
	</ContextMenu.Trigger>
	<ContextMenu.Content>
		<Reactions onReact={react} />
		<ContextMenu.Item icon="arrow-up" onPress={reply}>
			Reply
		</ContextMenu.Item>
		<ContextMenu.Item icon="copy" onPress={copy}>
			Copy
		</ContextMenu.Item>
		<ContextMenu.Item icon="trash" destructive onPress={remove}>
			Delete
		</ContextMenu.Item>
	</ContextMenu.Content>
</ContextMenu.Root>

Row actions in a list

A long press on a row, as an alternative to swipe actions that is easier to discover.

Roadmap.pdf
Moodboard.png4.2 MB

Open with…

Move

Rename

<ContextMenu.Root>
	<ContextMenu.Trigger>
		<Item onPress={() => open(file)}>…</Item>
	</ContextMenu.Trigger>
	<ContextMenu.Content>
		<ContextMenu.Sub label="Open with…">{apps.map(renderApp)}</ContextMenu.Sub>
		<ContextMenu.Item icon="archive" onPress={move}>
			Move
		</ContextMenu.Item>
		<ContextMenu.Item icon="edit" disabled={file.locked} onPress={rename}>
			Rename
		</ContextMenu.Item>
	</ContextMenu.Content>
</ContextMenu.Root>