Quick Answer: A React Native morphing tray like Morphlet provides a highly performant, native-driven floating UI component that dynamically transitions its size and shape. Built specifically for the React Native New Architecture (Fabric), it offloads layout calculations to native threads, eliminating the frame drops common with JavaScript-driven layout animations.
Building fluid, interactive floating menus that morph from a tiny button into a full-featured modal is notoriously difficult in cross-platform development. If you have ever tried to animate a layout change using standard JavaScript-driven libraries, you know the pain of dropped frames and visible stuttering. This is where a React Native morphing tray changes the game. By utilizing the native layout engines of iOS and Android, the morphlet library allows developers to build highly responsive, spring-animated floating trays that feel indistinguishable from native Apple or Google system components.
The Architecture Behind Morphlet: Why Native Beats JS-Thread Animation
Most animation tutorials tell you to use react-native-reanimated for every single transition. While Reanimated is excellent for transform-based animations (like scale, translation, and rotation), it struggles with layout-based animations where the actual width, height, or flex properties of a component change. When you change layout properties in JavaScript, the React Native bridge must calculate the new layout, pass it to the native side, and redraw the screen.
I have seen production apps stutter when trying to morph a floating action button into a full-screen menu while simultaneously fetching API data. The JS thread drops to 15 FPS, causing a jarring visual jump. Why does this happen? It happens because the JS thread is blocked by heavy data processing or rendering a complex list like FlashList.
If you are wondering how to build fluid animations in React Native without dropping frames, the answer lies in native layout offloading. Morphlet bypasses this bottleneck entirely. It is built from the ground up for the React Native New Architecture, utilizing the Fabric renderer and JSI (JavaScript Interface). Instead of calculating layout changes on the JS thread, Morphlet registers the layout transition directly with the native platform's layout engine (Yoga on Android and Auto Layout on iOS). According to performance benchmarks on the Fabric renderer, offloading layout transitions to native C++ components reduces frame drops by up to 40% compared to JS-driven layout animations.
This architectural shift means that even if your JavaScript thread is completely locked up processing a massive JSON payload, your floating tray will still morph, slide, and bounce at a buttery-smooth 120Hz on modern devices.
That said, there's a real catch here that you need to understand before integrating it into your codebase.
Setting Up Morphlet in Expo and Bare React Native
Because Morphlet relies on native C++ code and the New Architecture, you cannot simply drop it into an old React Native project and expect it to work. It requires React Native 0.74 or higher with the New Architecture explicitly enabled.
To install Morphlet in a bare React Native project, run:
npm install morphlet
cd ios && bundle exec pod install
For Expo developers, you must use a development build. Morphlet contains custom native code, meaning it will not run inside the standard Expo Go client. You will need to configure your app.json and generate a custom development build:
npx expo run:ios
# or
npx expo run:android
Let's look at a basic implementation of a native floating tray component for React Native Expo or bare projects:
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { Tray } from 'morphlet';
export function FloatingActionTray() {
return (
<Tray.Root>
<Tray.Trigger>
<Text style={styles.triggerText}>Quick Actions</Text>
</Tray.Trigger>
<Tray.Content style={styles.content}>
<Tray.Header>
<Tray.Title style={styles.title}>Select an Option</Tray.Title>
</Tray.Header>
<Tray.Body>
<Tray.Description style={styles.description}>
Choose a task to begin. The tray will morph to fit the content.
</Tray.Description>
</Tray.Body>
<Tray.Footer>
<Tray.Close>
<Text style={styles.closeText}>Cancel</Text>
</Tray.Close>
</Tray.Footer>
</Tray.Content>
</Tray.Root>
);
}
const styles = StyleSheet.create({
triggerText: { padding: 12, color: '#007AFF', fontWeight: '600' },
content: { backgroundColor: '#ffffff', borderRadius: 24 },
title: { fontSize: 18, fontWeight: 'bold' },
description: { fontSize: 14, color: '#666', marginVertical: 8 },
closeText: { color: '#FF3B30', padding: 12 }
});
This declarative API mirrors modern web libraries like Radix UI or Ariakit, making it highly intuitive. But the real power of Morphlet lies in how it handles multi-step workflows.
Here's where it gets interesting...
Mastering the React Native Morphing Tray: Advanced View Transitions
A common UX pattern is the multi-step modal. For example, a user taps "Settings", the tray opens, they tap "Security", and the tray morphs to a new height and width to accommodate the security options.
In a standard React Native app, you would have to manually calculate the height of each view, animate the container height using Reanimated, and handle the transition state. If you get the timing wrong, the content overflows or clips during the transition.
Morphlet solves this with <Tray.View> and the useTray hook. You define named views, and Morphlet automatically calculates the layout differences and morphs the container to fit the active view.
import { Tray, useTray } from 'morphlet';
import { View, Button, Text } from 'react-native';
function MainMenu() {
const { setView } = useTray();
return (
<View>
<Text>Main Menu Content</Text>
<Button title="Go to Details" onPress={() => setView('details')} />
</View>
);
}
function DetailMenu() {
const { goBack } = useTray();
return (
<View>
<Text>Detailed Settings (This view is much taller!)</Text>
<Button title="Back" onPress={() => goBack()} />
</View>
);
}
export function AdvancedTray() {
return (
<Tray.Root defaultView="main">
<Tray.Trigger><Text>Open Menu</Text></Tray.Trigger>
<Tray.Content>
<Tray.Body>
<Tray.View name="main">
<MainMenu />
</Tray.View>
<Tray.View name="details" fullScreen>
<DetailMenu />
</Tray.View>
</Tray.Body>
</Tray.Content>
</Tray.Root>
);
}
When transitioning from main to details, Morphlet doesn't just slide the new view in. It morphs the bounding box of the tray using native spring physics. If the details view is taller, the tray expands downward or upward smoothly.
You can also use the <Tray.Morph> wrapper. Wrap any changing value (like a dynamic text string or an image) in <Tray.Morph value={someValue}>, and Morphlet will transition the layout of the children whenever that value changes.
This next part trips people up every time, so let's look at how to customize those physics.
Fine-Tuning Spring Physics and Custom Presets
Out of the box, Morphlet provides four animation presets: default, smooth, snappy, and bouncy. While bouncy looks great in design mockups, it can feel sluggish or distracting in a utility-focused application.
If you want to build a truly premium experience, you should customize the spring physics. Morphlet allows you to pass custom spring configurations directly to the animation prop on <Tray.Root>:
<Tray.Root
animation={{
preset: 'smooth',
morph: {
response: 0.5, // Speed of the spring (lower is faster)
dampingFraction: 0.85 // Bounce control (1.0 is no bounce, < 1.0 bounces)
}
}}
>
{/* Tray Components */}
</Tray.Root>
The response parameter controls how quickly the spring responds to layout changes, while dampingFraction dictates how much it oscillates before settling.
A common mistake is setting the dampingFraction too low (e.g., 0.4). While a highly elastic bounce looks cool on a landing page, it violates accessibility guidelines (specifically WCAG 2.1 Success Criterion 2.3.3: Animation from Interactions), as excessive movement can cause motion sickness or distract users with cognitive disabilities. Keep your damping fraction between 0.8 and 0.95 for a professional, native-feeling bounce.
Let's look at how Morphlet stacks up against other popular floating UI libraries in the React Native ecosystem.
Comparing Floating UI Solutions in React Native
To understand where Morphlet fits, we need to compare it to the industry standards: react-native-reanimated and @gorhom/bottom-sheet.
| Feature | Morphlet | Gorhom Bottom Sheet | Custom Reanimated |
|---|---|---|---|
| Architecture | Native (Fabric/C++) | JS / Reanimated | JS / Reanimated |
| Layout Morphing | Automatic (Native) | Manual Height Calculation | Complex Custom Code |
| Performance | 120 FPS (Thread-Safe) | High (JS-Thread Dependent) | High (JS-Thread Dependent) |
| Expo Go Support | No (Requires Dev Build) | Yes | Yes |
| API Style | Declarative (Radix-like) | Imperative / Ref-based | Imperative / Custom |
As a Reanimated alternative, Morphlet focuses specifically on layout-driven transitions. While @gorhom/bottom-sheet is the gold standard for bottom sheets that drag up from the bottom of the screen, it is not designed to morph dynamically from a floating button in the center of the screen. Trying to force a bottom sheet to behave like a floating tray usually results in messy layout hacks and fragile coordinate calculations.
If you need a traditional bottom sheet with multiple snap points, stick with Gorhom's library. But if you want to build a fluid, floating action menu that expands, morphs, and adapts to dynamic content, Morphlet is the superior tool.
Frequently Asked Questions
What is a React Native morphing tray?
A React Native morphing tray is a floating UI component that dynamically adjusts its dimensions and position using native spring physics. It allows developers to create fluid transitions between different views or content sizes without experiencing the layout stuttering common in JavaScript-based animation libraries.
How does a React Native morphing tray handle keyboard avoidance?
Because Morphlet is built on the React Native New Architecture and utilizes native layout engines, it integrates directly with native keyboard avoidance mechanisms. The tray automatically adjusts its position and morphs its height when the keyboard opens, preventing input fields from being obscured.
Why does my Morphlet tray crash on older Android devices?
Morphlet requires Android API level 24 or higher. If your application supports older Android versions, Morphlet will fail to initialize on those devices. You must ensure your minSdkVersion is set to 24 in your project's android/build.gradle file.
Can I use Morphlet without enabling the React Native New Architecture?
No, Morphlet relies on the Fabric renderer and JSI to achieve its high-performance native layout morphing. It cannot be used in projects running the legacy architecture. You must upgrade to React Native 0.74+ and enable the New Architecture.
Elevating Your Mobile UX
Implementing a React Native morphing tray like Morphlet is one of the fastest ways to elevate the user experience of your mobile application from "good enough" to truly premium. By offloading layout calculations to the native thread, you ensure your animations remain fluid even under heavy system load. If you are currently building a complex multi-step form or a floating action menu, try replacing your custom Reanimated layout animations with Morphlet this week and note the difference in frame rates. For more advanced UI patterns, read our breakdown of React Native New Architecture Performance Optimization next.