Expo UI and Router for iOS
Components

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

Modifiers for any view

Sources

The original Expo docs for this page:

On this page