Actions

ContextMenu

A long-press menu on any React Native content, with a custom preview on iOS.

iOSUIContextMenuInteractionAndroidCompose DropdownMenu on a long pressReact Native tier — renders anywhere
iOSUIContextMenuInteraction
AndroidCompose DropdownMenu on a long press

Import

ts
import {ContextMenu} from '@hzblj/kara-ui'

Examples

A button inside the trigger

tsx
<ContextMenu actions={actions}>
  <ProductCard />
  <ContextMenu.Exclusion>
    <Button label="Add to bag" onPress={add} />
  </ContextMenu.Exclusion>
</ContextMenu>

Props

A context menu on a React Native subtree — a card in a list, say — opened by a long press. iOS: a UIContextMenuInteraction, with an optional React Native preview lifted while the menu is open and a callback for a tap on it. Android, where Material has no lifted preview: a ripple and the long-press haptic, then a Material dropdown where the finger is, with the trigger highlighted while it is open. The trigger is ordinary React Native layout, so it needs no Screen or Island.

Also takes the React Native `sx` props.

PropTypeDescription
actions*MenuAction[]

The menu's actions, top to bottom.

children*ReactNode

The trigger, laid out as usual.

onPreviewPress() => void

iOS: the user tapped the lifted preview, committing to it — open the detail the card stands for.

previewReactNode

iOS: lifted in place of the trigger while the menu is open — a larger version of the card, say. A snapshot of the trigger when unset. Mounted only once a finger has rested on the trigger, so a list of cards does not pay for every preview up front.

previewTransition'dismiss' | 'pop' | 'zoom'

iOS: how the preview leaves once tapped, before onPreviewPress. zoom: its photo grows straight into the pushed screen, the way the App Store expands a preview — needs a ZoomTransition.Source (@hzblj/kara-ui/router) in the trigger, a preview and iOS 18, and shrinks back like dismiss otherwise. pop: UIKit's default, the preview flies out. dismiss: it shrinks back onto the trigger first. Default: zoom when the trigger holds a ZoomTransition.Source, pop otherwise.

testIDstring

Test identifier — on the native view around the trigger on iOS, on the trigger's Pressable on Android.

actions*
MenuAction[]

The menu's actions, top to bottom.

children*
ReactNode

The trigger, laid out as usual.

onPreviewPress
() => void

iOS: the user tapped the lifted preview, committing to it — open the detail the card stands for.

preview
ReactNode

iOS: lifted in place of the trigger while the menu is open — a larger version of the card, say. A snapshot of the trigger when unset. Mounted only once a finger has rested on the trigger, so a list of cards does not pay for every preview up front.

previewTransition
'dismiss' | 'pop' | 'zoom'

iOS: how the preview leaves once tapped, before onPreviewPress. zoom: its photo grows straight into the pushed screen, the way the App Store expands a preview — needs a ZoomTransition.Source (@hzblj/kara-ui/router) in the trigger, a preview and iOS 18, and shrinks back like dismiss otherwise. pop: UIKit's default, the preview flies out. dismiss: it shrinks back onto the trigger first. Default: zoom when the trigger holds a ZoomTransition.Source, pop otherwise.

testID
string

Test identifier — on the native view around the trigger on iOS, on the trigger's Pressable on Android.

Platform notes

  • The trigger is ordinary React Native layout, so it needs no Screen or Island, and takes the view sx props.
  • iOS lifts preview (or a snapshot of the trigger) above the menu. Material has no lifted preview: Android ripples, plays the long-press haptic and opens a dropdown where the finger is.
  • With a ZoomTransition.Source in the trigger, a tap on the iOS preview zooms its photo straight into the pushed screen.