Skip to content

About

Morph one image into another with a smooth gooey effect.

Topics

Resources

Code of conduct

Contributing

Stars

126 stars

Watchers

1 watching

Forks

Repository files navigation

react-native-morph-view

morph_compressed.mov

A React Native view that smoothly morphs one image into another. Flip a single toggle prop and the two shapes blend together with a native shader for a fluid, gooey transition.

  • Real gooey morph on both platforms — a blur + alpha-threshold shader (Metal on iOS, AGSL on Android) fuses the two silhouettes instead of crossfading them
  • One prop drives everything — flip toggle and the view animates from from to to and back; no imperative animation code
  • Works with any image — bundled assets (require(...)), remote URLs, or file URIs, via the familiar React Native image source shape
  • Gooey outline — an optional border traces the morphing silhouette, not a rectangular CSS box around the view
  • Tunable goop — blurRadius controls how molten the midpoint looks, duration controls the timing
  • One API, three platforms — iOS, Android, and Web share the same <MorphView />; web gets an animated crossfade fallback
  • Fabric / New Architecture only

Installation

npm install react-native-morph-view

Requirements

  • React Native 0.76+ (New Architecture / Fabric)
  • iOS 15.1+
  • Android API 33+ (AGSL runtime shaders require Android 13+; older devices fall back to a crossfade)

Web setup

On web, MorphView resolves image sources through Image.resolveAssetSource, which React Native Web doesn't provide. If you target web, polyfill it before rendering: necolas/react-native-web#2814

Usage

Render a MorphView, give it two images, and flip toggle to play the morph:

import { useState } from 'react';
import { MorphView } from 'react-native-morph-view';

export default function App() {
  const [on, setOn] = useState(false);

  return (
    <MorphView
      toggle={on}
      from={require('./assets/heart.png')}
      to={require('./assets/star.png')}
      blurRadius={24}
      tintColor="#FF3B6B"
      style={{ width: 200, height: 200 }}
      onTouchEnd={() => setOn((v) => !v)}
    />
  );
}

MorphView extends ViewProps, so layout, style, touch handlers, and accessibility props all work as usual. Use a remote or file image anywhere you'd use a bundled one:

<MorphView
  toggle={on}
  from={require('./assets/coffee.png')}
  to={{ uri: 'https://example.com/battery.png' }}
  style={{ width: 160, height: 160 }}
/>

API

<MorphView>

Renders both images and animates the gooey morph between them whenever toggle changes.

Prop Type Default Description
toggle boolean — Which image is shown. false → from, true → to. Flipping it plays the morph in that direction. Required.
from MorphImageSource — Image shown while toggle is false. require(...), { uri }, or a file URI. Required.
to MorphImageSource — Image shown while toggle is true. Required.
blurRadius number 24 Peak blur applied at the midpoint of the morph (points/dp). Larger = goopier.
duration number 600 Morph duration in milliseconds.
tintColor ColorValue — Optional template tint applied to both images — recolors opaque pixels, mimicking the SF-Symbol look. Omit to keep each image's own colors.
borderColor ColorValue — Color of the gooey outline that hugs the morphing silhouette. Drawn only when borderColor is set and borderWidth is greater than 0.
borderWidth number 0 Thickness of the gooey outline in points. 0 disables it.
...ViewProps ViewProps — All standard View props — style, onTouchEnd, accessibilityLabel, etc.

MorphImageSource

type MorphImageSource = number | { uri: string };

A bundled asset (require('./x.png') resolves to a number) or a remote / file image ({ uri: 'https://…' }).

How it works

Both images are rendered natively, then blended through an alpha-threshold shader. As toggle flips, each image's blur ramps up toward blurRadius at the midpoint and back down, while the threshold step re-sharpens the combined alpha — so overlapping blurred regions fuse into a single connected blob (the "metaball" effect) before resolving into the target shape.

  • iOS runs the effect as a Metal shader.
  • Android runs it as an AGSL RuntimeShader (Android 13+).
  • Web can't run the native shader, so it approximates the morph with an ease-in-out crossfade plus a static soft blur on each image. No metaball fusing — for that, run on iOS or Android.

Tips

  • For the cleanest goo, use images with transparent backgrounds and solid, opaque shapes — the shader works on the alpha channel.
  • Pair MorphView with LayoutAnimation if the surrounding layout also changes when you toggle, so the whole transition stays in sync.
  • Increase blurRadius for blobbier, more liquid transitions; decrease it for a tighter, snappier morph.
  • Use tintColor to drive monochrome glyphs from your theme without shipping pre-colored assets.

Examples

A runnable demo lives in example/ — an Expo app that morphs between shapes, photos, and a multi-image gallery, with live controls for blurRadius, duration, tintColor, and the gooey border.

yarn install
yarn example ios      # or: yarn example android / yarn example web
// Morph through any-length list of images
const GALLERY = [
  require('./assets/blob.png'),
  require('./assets/star.png'),
  require('./assets/expo.png'),
];

// Gooey outline + template tint
<MorphView
  toggle={on}
  from={GALLERY[0]}
  to={GALLERY[1]}
  tintColor="#0A84FF"
  borderColor="#0A84FF"
  borderWidth={3}
  blurRadius={32}
  style={{ width: 200, height: 200 }}
/>;

Contributing

License

MIT

About

Morph one image into another with a smooth gooey effect.

Topics

Resources

Code of conduct

Contributing

Stars

126 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages