npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-reanimated-pager-view

v4.0.1

Published

High-performance PagerView component for React Native, built on react-native-reanimated v4

Downloads

631

Readme

react-native-reanimated-pager-view

High-performance PagerView component for React Native, built on react-native-reanimated v4 and react-native-gesture-handler. Enhanced customization capabilities and smooth animations running on the native thread.

✨ Features

  • 🚀 High Performance - uses react-native-reanimated v4 for smooth animations on the native thread
  • 🎨 Full Customization - configure gestures, animations and behavior through callbacks
  • Custom Page Animations - create stunning page transitions with pageStyleInterpolator
  • ↔️ Overscroll effects - add an overscroll effect when reaching the edges (see createBounceScrollOffsetInterpolator for iOS-like bounce effect)
  • 📱 Platform Support - iOS and Android
  • 🔧 TypeScript - complete type safety out of the box
  • 🎯 Lazy loading - deferred page loading for performance optimization
  • 👀 Visibility tracking - track visible pages on screen
  • 🔄 Dynamic management - add/remove pages with automatic positioning
  • 📱 Vertical Mode - support for vertical scrolling

https://github.com/user-attachments/assets/121e4339-e74d-4946-8d73-4760cc221d34

🗂️ Table of Contents

🚀 Quick Start

Installation

# npm
npm install react-native-reanimated-pager-view react-native-reanimated react-native-gesture-handler

# yarn
yarn add react-native-reanimated-pager-view react-native-reanimated react-native-gesture-handler

# pnpm
pnpm add react-native-reanimated-pager-view react-native-reanimated react-native-gesture-handler

# bun
bun add react-native-reanimated-pager-view react-native-reanimated react-native-gesture-handler

Follow the installation instructions for react-native-reanimated and react-native-gesture-handler.

Basic Usage

import React, { useCallback, useMemo } from 'react';
import { View, Text } from 'react-native';
import {
  PagerView,
  createBounceScrollOffsetInterpolator,
} from 'react-native-reanimated-pager-view';

const pages = [
  { id: 'page1', color: '#ff6b6b', title: 'Page 1' },
  { id: 'page2', color: '#4ecdc4', title: 'Page 2' },
  { id: 'page3', color: '#45b7d1', title: 'Page 3' },
];

// Optional: Add bounce effect
const bounceInterpolator = createBounceScrollOffsetInterpolator();

export default function App() {
  const children = useMemo(
    () =>
      pages.map((page) => (
        <View key={page.id} style={{ flex: 1, backgroundColor: page.color }}>
          <Text>{page.title}</Text>
        </View>
      )),
    [],
  );

  return (
    <PagerView scrollOffsetInterpolator={bounceInterpolator}>
      {children}
    </PagerView>
  );
}

⚠️ Important: Nested Scrollable Components

When using scrollable components inside PagerView pages, you need to prevent gesture conflicts. The library provides a ScrollableWrapper component that automatically handles this.

📚 API Documentation

Basic Properties

| Property | Type | Default | Description | | --------------- | ---------------------------- | -------------- | -------------------------------------------- | | children | ReactNode[] | - | Array of pages to display | | style | StyleProp<ViewStyle> \| PagerStyleFn | - | Style object/array or function for the container | | initialPage | number | 0 | Initial page number | | scrollEnabled | boolean | true | Enable pager scrolling | | pageMargin | number | 0 | Margin between pages | | orientation | 'horizontal' \| 'vertical' | 'horizontal' | Scrolling direction (horizontal or vertical) |

Animation Customization

| Property | Type | Default | Description | | -------------------------- | -------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- | | pageStyleInterpolator | PageStyleInterpolator | - | Custom function for animating pages based on scroll position (must be a worklet) | | scrollOffsetInterpolator | ScrollOffsetInterpolator | - | Custom function for modifying scroll behavior and overscroll effects | | scrollToPageSpringConfig | ScrollToPageSpringConfig | - | Configure spring parameters used when scrolling to the target page after a drag or setPage (must be a worklet) |

Callbacks

Note: Only onPageScroll should be a worklet for optimal performance. All other callbacks are called via runOnJS.

| Property | Type | Description | | -------------------------- | ---------------------------- | ------------------------------------------------------------------------ | | onPageSelected | (page: number) => void | Called when a new page is selected | | onPageScroll | (position: number) => void | Called during scrolling with current scroll position (should be worklet) | | onPageScrollStateChanged | (state) => void | State change during scrolling | | onDragStart | () => void | Start of drag gesture | | onDragEnd | () => void | End of drag gesture | | onInitialMeasure | () => void | Called after initial measurement of container dimensions |

Gesture Customization

| Property | Type | Default | Description | | ---------------------------------------- | ------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | gestureConfiguration | (gesture: Gesture) => Gesture | - | Function to customize pan gesture | | activationDistance | number | 10 | Minimum distance before activation | | gestureDirectionToleranceDeg | number | 45 | Maximum diagonal angle (in degrees) allowed between swipe and pager axis before PagerView rejects the gesture. Increase to allow more diagonal swipes; decrease to require straighter swipes. | | failActivationWhenExceedingStartEdge | boolean | false | Fail gesture activation when swiping beyond start edge. For example, this is useful for resolving conflicts with fullscreen swipe-back navigation gesture | | failActivationWhenExceedingEndEdge | boolean | false | Fail gesture activation when swiping beyond end edge. Allows parent gestures to handle | | hitSlop | HitSlop | - | Define touchable area for gesture recognition | | blockParentScrollableWrapperActivation | boolean (iOS) | false; iOS only | When true, PagerView's pan gesture actively blocks activation of the nearest parent ScrollableWrapper gesture (if any). Use to capture swipes while parent scrollable is still decelerating (momentum) | | blocksExternalGesture | ExternalGesture[] | - | Inverse (many-to-one) relation of requireExternalGestureToFail. Prevents listed gestures from activating simultaneously; PagerView gesture takes priority | | panVelocityThreshold | number | 500 | Minimum velocity for page switching. Note: page will switch if scrolled past 50% regardless of velocity | | pageActivationThreshold | number | 0.8 | Visibility percentage for page activation |

Performance

| Property | Type | Default | Description | | -------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------ | | lazy | boolean | false | Deferred page loading | | lazyPageLimit | number | 1 | Number of pages to preload | | removeClippedPages | boolean | true | Remove invisible pages from Native Tree. Note: enabled by default but may cause issues, use with caution ⚠️. |

Page Management

| Property | Type | Default | Description | | --------------------------------- | ---------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | holdCurrentPageOnChildrenUpdate | boolean | false | Maintain current page position when children array changes. Useful when pages are dynamically added/removed and you want to stay on the same logical page | | estimatedSize | number \| null | Dimensions.get('window').width | Expected container size (width for horizontal, height for vertical orientation). If null, size will be measured on first render (may cause layout shift) |

Ref Methods

| Method | Type | Description | | ------------------------- | ------------------------ | ---------------------------------- | | setPage | (page: number) => void | Navigate to page with animation | | setPageWithoutAnimation | (page: number) => void | Navigate to page without animation |

↔️ Scroll Offset Interpolation

The library provides a built-in createBounceScrollOffsetInterpolator utility for creating iOS-like bounce effects:

import { createBounceScrollOffsetInterpolator } from 'react-native-reanimated-pager-view';

const bounceInterpolator = createBounceScrollOffsetInterpolator({
  resistanceFactor: 0.7, // Overscroll resistance (0-1)
  threshold: 0.3, // Threshold for callback trigger
  onThresholdReached: ({ side }) => {
    console.log(`Reached ${side} boundary`);
  },
  triggerThresholdCallbackOnlyOnce: false, // Trigger callback multiple times or once per gesture
});

<PagerView scrollOffsetInterpolator={bounceInterpolator} />;

You can write your own scroll offset interpolator by following the same pattern as createBounceScrollOffsetInterpolator 🔥.

💄 Dynamic Styling

The style prop can accept a function for dynamic styling based on scroll position:

const rubberBandStyle: PagerStyleFn = ({ scrollPosition }) => {
  'worklet';

  return {
    transformOrigin: scrollPosition < 0 ? 'top' : 'bottom',
    transform: [
      {
        scaleY: interpolate(
          scrollPosition,
          [-1, 0, pages.length - 1, pages.length],
          [1.25, 1, 1, 1.25],
        ),
      },
    ],
  };
};

<PagerView style={rubberBandStyle} />;

🔧 ScrollableWrapper Component

The ScrollableWrapper component solves gesture conflicts when using scrollable components inside PagerView pages. It automatically detects the PagerView's orientation and configures gesture priorities to prevent conflicts. Also, it provides Twitter/𝕏-like behavior where swiping to change pages smoothly interrupts internal scrolling and switches to PagerView gesture (just wrap everything that can scroll 🪄).

When to use ScrollableWrapper

Use ScrollableWrapper whenever you have scrollable content inside PagerView pages:

  • Vertical scrolls inside horizontal PagerView (FlatList, ScrollView, etc.)
  • Horizontal scrolls inside vertical PagerView (horizontal FlatList, carousel, etc.)
  • Any scrollable component that might conflict with PagerView gestures

Basic Usage

import { ScrollableWrapper } from 'react-native-reanimated-pager-view';

// For vertical scrolling content in horizontal PagerView
function FeedPage() {
  return (
    <ScrollableWrapper orientation="vertical">
      <FlatList
        data={posts}
        renderItem={({ item }) => <PostItem post={item} />}
      />
    </ScrollableWrapper>
  );
}

// For horizontal scrolling content in vertical PagerView
function HorizontalCarouselPage() {
  return (
    <ScrollableWrapper orientation="horizontal">
      <FlatList
        data={items}
        horizontal
        renderItem={({ item }) => <CarouselItem item={item} />}
      />
    </ScrollableWrapper>
  );
}

Advanced Usage with Multiple Scrollables

function ComplexPage() {
  return (
    <View style={{ flex: 1 }}>
      {/* Vertical main content */}
      <ScrollableWrapper orientation="vertical">
        <ScrollView>
          <Text>Main content that scrolls vertically</Text>

          {/* Horizontal carousel within vertical scroll */}
          <ScrollableWrapper orientation="horizontal">
            <FlatList
              data={carouselItems}
              horizontal
              renderItem={({ item }) => <CarouselItem item={item} />}
            />
          </ScrollableWrapper>

          <Text>More vertical content...</Text>
        </ScrollView>
      </ScrollableWrapper>
    </View>
  );
}

👀 Page Visibility Tracking

The library provides built-in support for tracking which pages are currently visible on screen. This is useful for analytics, lazy loading content, pausing/resuming videos, or any other visibility-dependent features.

import React, { useMemo } from 'react';
import { useIsOnscreenPage } from 'react-native-reanimated-pager-view';

const VISIBILITY_PAGES = [
  { id: 'page1', title: 'Page 1' },
  { id: 'page2', title: 'Page 2' },
  { id: 'page3', title: 'Page 3' },
];

const PageWithTracking = ({ data }) => {
  const isOnscreen = useIsOnscreenPage();

  return (
    <View>
      <Text>{data.title}</Text>
      <Text>Visible: {isOnscreen ? 'Yes' : 'No'}</Text>
    </View>
  );
};

const VisibilityTrackingExample = () => {
  const children = useMemo(
    () =>
      VISIBILITY_PAGES.map((page) => (
        <PageWithTracking key={page.id} data={page} />
      )),
    [],
  );

  return <PagerView>{children}</PagerView>;
};

📱 Vertical Mode

The PagerView supports vertical scrolling, perfect for creating any vertical page-based navigation.

import React, { useMemo } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { PagerView } from 'react-native-reanimated-pager-view';

const pages = [
  { id: 'page1', color: '#ff6b6b', title: 'Swipe Up' },
  { id: 'page2', color: '#4ecdc4', title: 'Keep Swiping' },
  { id: 'page3', color: '#45b7d1', title: 'Almost There' },
  { id: 'page4', color: '#96ceb4', title: 'Last Page!' },
];

// Create bounce interpolator for vertical mode
const bounceInterpolator = createBounceScrollOffsetInterpolator();

const VerticalExample = () => {
  const children = useMemo(
    () =>
      pages.map((page) => (
        <View
          key={page.id}
          style={[styles.page, { backgroundColor: page.color }]}
        >
          <Text style={styles.title}>{page.title}</Text>
        </View>
      )),
    [],
  );

  return (
    <PagerView
      style={styles.container}
      orientation="vertical"
      scrollOffsetInterpolator={bounceInterpolator}
    >
      {children}
    </PagerView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  page: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'white',
  },
});

🎯 Advanced Examples

Custom Page Animations

import React, { useMemo } from 'react';
import { interpolate } from 'react-native-reanimated';
import {
  PagerView,
  type PageStyleInterpolator,
} from 'react-native-reanimated-pager-view';

const ANIMATION_PAGES = [
  { id: 'page1', title: 'Page 1', color: '#ff6b6b' },
  { id: 'page2', title: 'Page 2', color: '#4ecdc4' },
  { id: 'page3', title: 'Page 3', color: '#45b7d1' },
];

const pageStyleInterpolator: PageStyleInterpolator = ({ pageOffset }) => {
  'worklet';

  const rotateY = interpolate(pageOffset, [-1, 0, 1], [60, 0, -60], 'clamp');
  const scale = interpolate(pageOffset, [-1, 0, 1], [0.8, 1, 0.8], 'clamp');

  return {
    transform: [{ perspective: 1000 }, { rotateY: `${rotateY}deg` }, { scale }],
  };
};

const CustomAnimationPager = () => {
  const children = useMemo(
    () =>
      ANIMATION_PAGES.map((page) => (
        <View
          key={page.id}
          style={{
            flex: 1,
            backgroundColor: page.color,
            justifyContent: 'center',
            alignItems: 'center',
          }}
        >
          <Text style={{ fontSize: 24, color: 'white', fontWeight: 'bold' }}>
            {page.title}
          </Text>
        </View>
      )),
    [],
  );

  return (
    <PagerView pageStyleInterpolator={pageStyleInterpolator}>
      {children}
    </PagerView>
  );
};

Lazy Loading

import React, { useMemo } from 'react';

const LAZY_PAGES = [
  { id: 'page1', title: 'Page 1', content: 'Heavy content 1' },
  { id: 'page2', title: 'Page 2', content: 'Heavy content 2' },
  { id: 'page3', title: 'Page 3', content: 'Heavy content 3' },
];

const LazyPagerExample = () => {
  const children = useMemo(
    () => LAZY_PAGES.map((page) => <LazyPage key={page.id} data={page} />),
    [],
  );

  return (
    <PagerView lazy={true} lazyPageLimit={1}>
      {children}
    </PagerView>
  );
};

Gesture Customization

import React, { useCallback, useMemo } from 'react';

const GESTURE_PAGES = [
  { id: 'page1', title: 'Page 1' },
  { id: 'page2', title: 'Page 2' },
  { id: 'page3', title: 'Page 3' },
];

const customGestureConfig = (gesture: Gesture) => {
  // Reduce gesture area
  return gesture.hitSlop(-10);
};

const CustomGesturePager = () => {
  const children = useMemo(
    () =>
      GESTURE_PAGES.map((page) => (
        <View key={page.id}>
          <Text>{page.title}</Text>
        </View>
      )),
    [],
  );

  return (
    <PagerView gestureConfiguration={customGestureConfig} scrollEnabled={true}>
      {children}
    </PagerView>
  );
};

🔧 React Navigation Integration

When using swipe-back navigation gesture, PagerView can interfere by capturing swipes from the screen edge. These props solve the conflict:

  • Use failActivationWhenExceedingStartEdge={true} when you want to allow fullscreen swipe-back gesture from first page (you should enable fullscreen swipe-back gesture in your navigation configuration too).
  • Use hitSlop to add area on the left edge of PagerView that would allow swipe-back gesture to be recognized. You probably want this value to be equal to the value of React Navigation's gestureResponseDistance prop.
import React, { useMemo } from 'react';
import { View, Text } from 'react-native';
import { PagerView } from 'react-native-reanimated-pager-view';

const pages = [
  { id: 'page1', color: '#ff6b6b', title: 'Page 1' },
  { id: 'page2', color: '#4ecdc4', title: 'Page 2' },
  { id: 'page3', color: '#45b7d1', title: 'Page 3' },
];

// Add 50px of non-swipeable area on the left edge
const pagerViewSwipeBackArea = { left: -50 };

export default function ScreenInStack() {
  const children = useMemo(
    () =>
      pages.map((page) => (
        <View key={page.id} style={{ flex: 1, backgroundColor: page.color }}>
          <Text>{page.title}</Text>
        </View>
      )),
    [],
  );

  return (
    <PagerView
      failActivationWhenExceedingStartEdge
      hitSlop={pagerViewSwipeBackArea}
    >
      {children}
    </PagerView>
  );
}

That's it! These two props ensure your PagerView works seamlessly with navigation swipe-back gesture.

You can use the same approach for modals with vertical swipes too! 🔥

⚡ Performance Optimization

Recommendations

  1. Use lazy loading for large number of pages
  2. Optimize children - use key props, memo, and consider wrapping all children in useMemo or extracting outside component
  3. Be careful with removeClippedPages - while enabled by default for memory optimization, it may cause issues with complex layouts. Disable if you encounter problems, but expect potential performance impact

Optimization Example

// Extract pages outside component to avoid recreation
const STATIC_PAGES = [
  { id: 'page1', title: 'Page 1', color: '#ff6b6b' },
  { id: 'page2', title: 'Page 2', color: '#4ecdc4' },
  { id: 'page3', title: 'Page 3', color: '#45b7d1' },
];

const MemoizedPage = memo(({ data }) => (
  <View style={{ flex: 1, backgroundColor: data.color }}>
    <Text>{data.title}</Text>
  </View>
));

const OptimizedPager = memo(() => {
  // Memoize children to prevent recreation on re-renders
  const children = useMemo(
    () =>
      STATIC_PAGES.map((page) => <MemoizedPage key={page.id} data={page} />),
    [],
  );

  // Memoize callbacks
  const handlePageSelected = useCallback((page: number) => {
    console.log('Page selected:', page);
  }, []);

  return (
    <PagerView
      lazy={true}
      lazyPageLimit={1}
      removeClippedPages={true}
      estimatedSize={screenWidth}
      onPageSelected={handlePageSelected}
    >
      {children}
    </PagerView>
  );
});

🙏 Acknowledgments


Made with ❤️ for the React Native community