Components

MorphPopover

A trigger button that morphs into a floating panel, animated with Reanimated.

iOSAndroidExpo Go
Preview MorphPopover on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-morph-popover
Required Packages(1 package)
pnpm add react-native-reanimated

The trigger is measured with measureInWindow. A transparent Modal then shows a panel that starts at the trigger's size and corner radius and grows to the content size, while the content fades and slides in. Closing plays the animation in reverse. Tapping outside the panel closes it.

The panel opens on side and flips to the other side when there is not enough room. It is centred on the trigger and kept 16pt from the screen edges. If the content changes height while open, the panel resizes in place. When the system's reduce-motion setting is on, the panel appears without animation.

Usage

import { MorphPopover } from '@/components/docyrus-native/morph-popover';

<MorphPopover>
  <Text className="text-sm font-semibold text-white dark:text-neutral-900">Quick actions</Text>
</MorphPopover>
<MorphPopover
  side="bottom"
  triggerWidth={140}
  triggerHeight={40}
  contentWidth={280}
  bgClassName="bg-primary"
  isOpen={open}
  onOpenChange={setOpen}
  trigger={<Text className="text-primary-foreground">New item</Text>}>
  <Button onPress={() => setOpen(false)}>Task</Button>
</MorphPopover>

Content text does not inherit colour on React Native, so style the children to contrast with bgClassName. The default background is bg-neutral-900 in light mode and bg-neutral-100 in dark mode.

API Reference

PropTypeDefaultDescription
childrenReactNode-Panel content (required)
triggerReactNode-Trigger content (default: a plus icon)
triggerSizenumber44Square trigger size (px)
triggerWidthnumber-Trigger width (overrides triggerSize)
triggerHeightnumber-Trigger height (overrides triggerSize)
triggerRadiusnumbertriggerSize / 2 (6 with custom dimensions)Trigger corner radius
triggerClassNamestring-Trigger classes
disabledbooleanfalseDisables the trigger
isOpenboolean-Controlled open state
onOpenChange(open: boolean) => void-Open state change callback
side'top' | 'bottom''top'Preferred side; flips when there is not enough room
sideOffsetnumber24Gap between trigger and panel (px)
contentWidthnumber240Panel width, clamped to screen width minus a 16pt gutter
speednumber0.25Animation duration in seconds
bgClassNamestring'bg-neutral-900 dark:bg-neutral-100'Background classes shared by trigger and panel
contentClassNamestring-Panel content classes
classNamestring-Wrapper classes

Type Exports

TypeDescription
MorphPopoverPropsProps for MorphPopover

Differences from web

  • GSAP is replaced by Reanimated. The SVG "goo" filter has no React Native equivalent and is not reproduced.
  • speed is still in seconds.
  • Outside clicks and Escape become a backdrop tap and the Android back button.

On this page