Skip to content
Native Animation
ButtonCardGestureFormKeyboard

Morphing Action Button

A floating action button that becomes an editable card while the screen recedes behind it.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI.

npx nativeanimation add morphing-action-button

About this animation

A floating action button that becomes an editable card while the screen recedes behind it.

Why this one

The button and card are one continuous surface rather than two views crossfading. The same interruptible progress value drives its position, dimensions, color, backdrop depth, icon, content, and drag-to-dismiss interaction. The expanded card also follows the software keyboard, preserving a 24-point gap instead of letting the form become obscured.

How it works

  1. The component measures its own container, so the closed button and open card adapt to split screens, tablets, and nested layouts.
  2. A single progress shared value springs between zero and one and interpolates the surface's position, width, height, radius, and background color.
  3. The background scales inside a rounded clipping view while a separate dimming veil fades in.
  4. Card content waits until 70% of the shape transition is complete, then fades and rises into place.
  5. A downward pan writes directly to the same progress value; release velocity and position decide whether to close or spring back open.
  6. Reanimated's keyboard height keeps the card's lower edge 24 points above the keyboard.

Install

npx expo install react-native-gesture-handler react-native-reanimated react-native-worklets

Usage

import MorphingActionButton from './MorphingActionButton';

export function NotesScreen() {
  return (
    <MorphingActionButton background={<NotesList />}>
      <NoteForm />
    </MorphingActionButton>
  );
}

Customize the surface and control it through the forwarded ref:

import { useRef } from 'react';
import MorphingActionButton, {
  type MorphingActionButtonRef,
} from './MorphingActionButton';

export function Composer() {
  const actionRef = useRef<MorphingActionButtonRef>(null);

  return (
    <MorphingActionButton
      ref={actionRef}
      background={<Dashboard />}
      buttonColor="#20202A"
      cardColor="#111827"
      cardWidth={0.92}
      cardHeight={340}
      backdropScale={0.93}
      onOpenChange={(open) => trackComposer(open)}
      accessibilityLabel="Compose"
    >
      <ComposerForm onDone={() => actionRef.current?.close()} />
    </MorphingActionButton>
  );
}

The forwarded ref exposes open() and close().

Props

PropTypeDefaultDescription
childrenReact.ReactNodeRequiredContent displayed inside the expanded card.
backgroundReact.ReactNode—Screen content behind the transforming surface.
buttonColorstring'#111111'Collapsed button color.
cardColorstring'#16161C'Expanded card color.
cardWidthnumber0.88Expanded width as a fraction of the container.
cardHeightnumber300Expanded height in points.
backdropScalenumber0.95Background scale while open.
onOpenChange(open: boolean) => void—Called when opening or closing starts.
accessibilityLabelstring'Create'Label for the floating action and expanded card.
styleStyleProp<ViewStyle>—Outer-container style.
testIDstring—Test identifier for the outer container.

Compatibility

  • Expo SDK 57
  • React Native 0.86
  • Reanimated 4.5 and Worklets 0.10
  • iOS: supported, not yet verified on a device
  • Android: supported, not yet verified on a device
  • Web: supported, not yet verified
  • Works in Expo Go because its native dependencies are included in the SDK

Accessibility

The action, close control, backdrop dismissal, form controls, and save action expose labels and roles. Opening moves screen-reader focus to the expanded card, and the close button provides a tap alternative to the downward gesture. With reduced motion enabled, opening, closing, and gesture settling snap directly to their destination while the keyboard avoidance remains active.