ContextMenu
A SwiftUI ContextMenu component for displaying context menus.
Expo UI ContextMenu matches the official SwiftUI contextMenu API and displays a menu when long-pressed. For single-tap menu interactions, use Menu instead.
Usage
Basic context menu
import { Host, ContextMenu, Button, Text } from '@expo/ui/swift-ui';
export default function BasicContextMenuExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Edit"
onPress={() => console.log('Edit')}
/>
<Button
label="Delete"
role="destructive"
onPress={() => console.log('Delete')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Context menu with system images
import { Host, ContextMenu, Button, Text } from '@expo/ui/swift-ui';
export default function ContextMenuWithImagesExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Share"
systemImage="square.and.arrow.up"
onPress={() => console.log('Share')}
/>
<Button
label="Favorite"
systemImage="heart"
onPress={() => console.log('Favorite')}
/>
<Button
label="Delete"
systemImage="trash"
role="destructive"
onPress={() => console.log('Delete')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Context menu with preview
Use ContextMenu.Preview to show a custom preview above the menu when opened.
import { View, Text as RNText } from 'react-native';
import {
Host,
ContextMenu,
Button,
RNHostView,
Text,
} from '@expo/ui/swift-ui';
export default function ContextMenuWithPreviewExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Edit"
onPress={() => console.log('Edit')}
/>
<Button
label="Delete"
role="destructive"
onPress={() => console.log('Delete')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
<ContextMenu.Preview>
<RNHostView matchContents>
<View
style={{
width: 200,
height: 100,
backgroundColor: '#f0f0f0',
padding: 16,
}}>
<RNText>Preview content</RNText>
</View>
</RNHostView>
</ContextMenu.Preview>
</ContextMenu>
</Host>
);
}Context menu with picker
import { useState } from 'react';
import {
Host,
ContextMenu,
Button,
Text,
Picker,
} from '@expo/ui/swift-ui';
import { pickerStyle, tag } from '@expo/ui/swift-ui/modifiers';
export default function ContextMenuWithPickerExample() {
const [selectedIndex, setSelectedIndex] = useState<
number | undefined
>(0);
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Action"
onPress={() => console.log('Action')}
/>
<Picker
label="Size"
modifiers={[pickerStyle('menu')]}
selection={selectedIndex}
onSelectionChange={setSelectedIndex}>
{['Small', 'Medium', 'Large'].map((option, index) => (
<Text key={index} modifiers={[tag(index)]}>
{option}
</Text>
))}
</Picker>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Context menu with sections
Use Section and Divider components to organize menu items.
import {
Host,
ContextMenu,
Button,
Text,
Section,
Divider,
} from '@expo/ui/swift-ui';
export default function ContextMenuWithSectionsExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Section title="Actions">
<Button
label="Edit"
onPress={() => console.log('Edit')}
/>
<Button
label="Duplicate"
onPress={() => console.log('Duplicate')}
/>
</Section>
<Divider />
<Button
label="Delete"
role="destructive"
onPress={() => console.log('Delete')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Disabled items
Use the disabled(true) modifier on a menu Button to render it greyed-out and non-interactive.
import { Host, ContextMenu, Button, Text } from '@expo/ui/swift-ui';
import { disabled } from '@expo/ui/swift-ui/modifiers';
export default function DisabledContextMenuItemExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Edit"
onPress={() => console.log('Edit')}
/>
<Button
label="Locked"
systemImage="lock"
modifiers={[disabled(true)]}
onPress={() => console.log('This never fires')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Selectable items (checkmarks)
A SwiftUI Toggle inside ContextMenu.Items automatically renders as a row with a leading SF Symbol and a trailing checkmark when isOn is true.
import { Host, ContextMenu, Toggle, Text } from '@expo/ui/swift-ui';
import { useState } from 'react';
export default function CheckmarkContextMenuItemExample() {
const [pinned, setPinned] = useState(false);
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Toggle
isOn={pinned}
label="Pin"
systemImage="pin"
onIsOnChange={setPinned}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}Nested context menus
Use nested ContextMenu components to create submenus.
import { Host, ContextMenu, Button, Text } from '@expo/ui/swift-ui';
export default function NestedContextMenuExample() {
return (
<Host style={{ flex: 1 }}>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Action"
onPress={() => console.log('Action')}
/>
<ContextMenu>
<ContextMenu.Items>
<Button
label="Sub Action 1"
onPress={() => console.log('Sub 1')}
/>
<Button
label="Sub Action 2"
onPress={() => console.log('Sub 2')}
/>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Button label="More Options" />
</ContextMenu.Trigger>
</ContextMenu>
</ContextMenu.Items>
<ContextMenu.Trigger>
<Text>Long press me</Text>
</ContextMenu.Trigger>
</ContextMenu>
</Host>
);
}API
import { ContextMenu } from '@expo/ui/swift-ui';Components
ContextMenu
Type: React.Element<ContextMenuProps>
ContextMenu allows you to create a context menu, which can be used to provide additional options to the user.
Props of the ContextMenu component.
ContextMenuProps
children
Type: ReactNode
The contents of the context menu. Should include ContextMenu.Trigger, ContextMenu.Items, and optionally ContextMenu.Preview.
Inherited props
Items
Type: React.Element<{ children: ReactNode }>
Items visible inside the context menu. It could be Section, Divider, Button, Toggle, Picker or even ContextMenu itself for nested menus. Remember to use components from the @expo/ui/swift-ui library.
Preview
Type: React.Element<{ children: ReactNode }>
The component visible above the menu when it is opened.
Trigger
Type: React.Element<{ children: ReactNode }>
The component visible all the time that triggers the context menu when long-pressed.
Modifiers
Use these modifiers in the modifiers prop of the SwiftUI version.
Modifiers for ContextMenu
menuActionDismissBehavioriOS 16.4+menuOrder
Modifiers for any view
accessibilityAddTraitsaccessibilityElementaccessibilityHiddenaccessibilityHintaccessibilityIdentifieraccessibilityInputLabelsaccessibilityLabelaccessibilityRemoveTraitsaccessibilityValueactivityBackgroundTintalignmentGuideallowsTighteninganimationaspectRatiobackgroundbackgroundOverlayblurboldborderbrightnessclippedclipShapecolorInvertcontainerBackgroundcontainerRelativeFrameiOS 17+containerShapecontentShapecontentTransitioniOS 16+contrastcornerRadiuscreateModifierdisableddynamicTypeSizeenvironmentfixedSizefontforegroundColorforegroundStyleframegeometryGroupiOS 17+glassEffectglassEffectIdgrayscalegridCellAnchoriOS 16+gridCellColumnsgridCellUnsizedAxesgridColumnAlignmentiOS 16+hiddenhueRotationidignoreSafeAreaimageScaleinvalidatableContentiOS 17+italickerninglayoutPrioritylineHeightiOS 26+lineLimitlineSpacingluminanceToAlphamaskmatchedGeometryEffectminimumScaleFactormonospacedDigitmultilineTextAlignmentoffsetonAppearonDisappearonGeometryChangeonLongPressGestureonTapGestureopacityoverlaypaddingprivacySensitiveredactedrotation3DEffectrotationEffectsaturationscaleEffectshadowstrikethroughstrokeBordertextCasetinttruncationModeunderlineunredactedwidgetURLzIndex
Sources
The original Expo docs for this page:
- ContextMenu (SwiftUI) (
source-of-truth/versions/v57.0.0/sdk/ui/swift-ui/contextmenu.md)