Description:
Motionify is a lightweight React Native component that creates smooth, scroll-driven UI animations using Reanimated 3. It runs animations on the UI thread to achieve a consistent 60 FPS, which results in a fluid user experience.
The library also automatically detects scroll direction and provides fine-grained control over animation thresholds. It is ideal for developers who need to build performant, scroll-aware interfaces without writing complex animation logic from scratch.
Features
- 👆 Automatic Direction Detection: Automatically determines the scroll direction (‘up’, ‘down’, or ‘idle’) with a configurable pixel threshold.
- 🎣 Simple Context and Hooks API: Provides a straightforward API with
MotionifyProviderand auseMotionifyhook to manage animation state. - 📦 Ready-Made Components: Includes
MotionifyViewandMotionifyBottomTabfor common scroll-driven UI patterns. - ⌨️ Typed API: Comes with a fully typed API for a better developer experience with TypeScript.
See It In Action
Use Cases
- Hiding Headers: Animate a header to slide out of view as the user scrolls down a list and reappear when they scroll up.
- Parallax Image Effects: Create a parallax effect where a background image moves at a different speed than the foreground content during a scroll.
- Animated Floating Action Buttons (FABs): Make a FAB hide or translate based on the scroll direction to clear up screen space.
- Dynamic Bottom Tab Bars: Configure a bottom tab bar to disappear when the user scrolls down and slide back into view when they scroll up.
How to Use It
1. Install the library in your project using your preferred package manager.
# npm
npm install react-native-motionify
# yarn
yarn add react-native-motionify2. Install dependencies.
npm install react-native-reanimated@^3.0.03. Wrap your application’s root component with MotionifyProvider. This provider sets up the context for all motionify components and hooks used within your app. You can configure a global threshold for scroll direction changes.
import { MotionifyProvider } from "react-native-motionify";
export default function App() {
return (
<MotionifyProvider threshold={10}>
<YourApp />
</MotionifyProvider>
);
}4. In any screen that contains a scrollable element, use the useMotionify hook to get the onScroll handler.
Attach this handler to your ScrollView, FlatList, or FlashList component. You must also set the scrollEventThrottle prop to 16 to ensure smooth animation updates.
import { ScrollView } from "react-native";
import { useMotionify } from "react-native-motionify";
function MyScreen() {
const { onScroll } = useMotionify();
return (
<ScrollView onScroll={onScroll} scrollEventThrottle={16}>
{/* Your screen content */}
</ScrollView>
);
}5. Use the MotionifyView component to animate any UI element based on scroll direction.
The example below shows a FAB that slides down and fades out when the user scrolls down.
import { MotionifyView } from "react-native-motionify";
function ScreenWithFAB() {
const { onScroll } = useMotionify();
return (
<ScrollView onScroll={onScroll} scrollEventThrottle={16}>
{/* Screen content */}
<MotionifyView
animatedY
fadeScale
hideOn="down"
translateRange={{ from: 0, to: 70 }}
>
<YourFABComponent />
</MotionifyView>
</ScrollView>
);
}6. To manage a bottom tab bar that hides on scroll, wrap it with the MotionifyBottomTab component. The hideOn prop specifies the scroll direction that triggers the hiding animation.
import { MotionifyBottomTab } from "react-native-motionify";
function AppShell() {
return (
<>
<MyScreen />
<MotionifyBottomTab hideOn="down" translateRange={{ from: 0, to: 90 }}>
<YourTabBarComponent />
</MotionifyBottomTab>
</>
);
}API Reference
Provider
<MotionifyProvider>: The main provider component.
threshold: A number representing the pixel distance to scroll before the direction changes. Default is8.supportIdle: A boolean that enables theidledirection state after a period of inactivity. Default isfalse.
Hook
useMotionify({ threshold?: number; supportIdle?: boolean }): A hook to access motion data and handlers.
- Returns:
scrollY: A ReanimatedSharedValue<number>for the current scroll position.direction: A string representing the current scroll direction (‘up’, ‘down’, ‘idle’).directionShared: ASharedValuefor the scroll direction.isScrolling: A boolean indicating if scrolling is active.onScroll: The scroll event handler for scrollable components.setThreshold(threshold): A function to update the threshold dynamically.setSupportIdle(enabled): A function to update idle support dynamically.
Components
<MotionifyView>: Animates child components based on scroll direction.- Props include
animatedY,fadeScale,hideOn,translateRange, andanimationDuration.
- Props include
<MotionifyViewWithInterpolation>: Animates styles based on scroll position interpolation.- Props include
interpolationsto define input and output ranges for styles.
- Props include
<MotionifyBottomTab>: A wrapper for a bottom tab bar that hides or shows on scroll.- Props include
hideOn,translateRange,exclude, andcurrentIdto control visibility on certain routes.
- Props include
<MotionifyBottomTabWithInterpolation>: A tab bar wrapper that translates its position based on scroll interpolation.
FAQs
Q: Do I need to install react-native-reanimated separately?
A: Yes, react-native-reanimated is a peer dependency and must be installed and configured in your project for React Native Motionify to work correctly.
Q: Why are my animations not working?
A: A common reason is forgetting to attach the onScroll handler from useMotionify() to your scrollable component or neglecting to set scrollEventThrottle={16}. Ensure both are correctly applied.
Q: Can I create custom animations without using the built-in components?
A: Yes, you can build your own animated components. The useMotionify hook provides scrollY and directionShared shared values, which you can use directly with Reanimated’s useAnimatedStyle to create custom animations.
Q: How does this library ensure good performance?
A: It leverages Reanimated 3 to run all animations and gesture calculations on the native UI thread, which prevents them from being blocked by expensive operations on the JavaScript thread.



