Scroll-Driven Animations with React Native Motionify

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 MotionifyProvider and a useMotionify hook to manage animation state.
  • 📦 Ready-Made Components: Includes MotionifyView and MotionifyBottomTab for common scroll-driven UI patterns.
  • ⌨️ Typed API: Comes with a fully typed API for a better developer experience with TypeScript.

See It In Action

From Motionify’s GitHub Repo

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-motionify

    2. Install dependencies.

    npm install react-native-reanimated@^3.0.0

    3. 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 is 8.
    • supportIdle: A boolean that enables the idle direction state after a period of inactivity. Default is false.

    Hook

    useMotionify({ threshold?: number; supportIdle?: boolean }): A hook to access motion data and handlers.

    • Returns:
      • scrollY: A Reanimated SharedValue<number> for the current scroll position.
      • direction: A string representing the current scroll direction (‘up’, ‘down’, ‘idle’).
      • directionShared: A SharedValue for 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, and animationDuration.
    • <MotionifyViewWithInterpolation>: Animates styles based on scroll position interpolation.
      • Props include interpolations to define input and output ranges for styles.
    • <MotionifyBottomTab>: A wrapper for a bottom tab bar that hides or shows on scroll.
      • Props include hideOn, translateRange, exclude, and currentId to control visibility on certain routes.
    • <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.

    Add Comment