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

@raymardev/react-native-intersection-observer

v1.1.0

Published

A powerful React Native hook for detecting scroll intersections with ScrollView, FlatList, and SectionList components

Readme

React Native Intersection Observer

npm version npm downloads License TypeScript React Native

A powerful and flexible React Native hook for detecting scroll intersections with ScrollView, FlatList, and SectionList components. This library provides an easy-to-use API similar to the web's Intersection Observer API, but specifically designed for React Native's scroll components.

📖 Documentation • 💡 Examples • 🐛 Issues • 💬 Discussions

✨ Features

  • 🎯 Multiple Detection Positions: Detect intersections at top, bottom, center, or custom element positions
  • 📱 Cross-Platform: Works with ScrollView, FlatList, and SectionList components
  • 🔧 TypeScript Support: Full TypeScript support with comprehensive type definitions
  • ⚡ Performance Optimized: Efficient scroll event handling with minimal re-renders
  • 🎨 Flexible API: Multiple convenience hooks for common use cases
  • 📊 Custom Thresholds: Configurable intersection thresholds
  • 🔄 Callback Support: Rich callback system for intersection events
  • 🚀 Zero Dependencies: No external dependencies, lightweight and fast
  • 📚 Comprehensive Docs: Detailed documentation and examples

📦 Installation

npm install @raymardev/react-native-intersection-observer

or

yarn add @raymardev/react-native-intersection-observer

Requirements

  • React Native 0.60+
  • React 16.8+ (hooks support required)
  • TypeScript 4.0+ (optional but recommended)

🚀 Quick Start

import React from 'react';
import { ScrollView, Text, View } from 'react-native';
import { useScrollToBottom } from '@raymardev/react-native-intersection-observer';

export default function App() {
  const { isIntersecting, ref, handleScroll } = useScrollToBottom(50, {
    type: 'scrollview',
    callbacks: {
      onIntersect: () => console.log('Reached bottom!'),
      onVisible: () => console.log('Left bottom area'),
    },
  });

  return (
    <ScrollView ref={ref} onScroll={handleScroll} scrollEventThrottle={16}>
      <View style={{ height: 2000 }}>
        <Text>Scroll to bottom to trigger intersection</Text>
        {isIntersecting && <Text>🎉 You reached the bottom!</Text>}
      </View>
    </ScrollView>
  );
}

📚 Documentation

🔧 API Reference

Core Hook

useIntersectionObserver(options)

The main hook that provides intersection detection functionality.

Parameters:

  • options (object):
    • threshold?: number - Distance threshold for intersection detection (default: 20)
    • position?: 'top' | 'bottom' | 'center' | 'custom' | 'element' - Position to detect (default: 'bottom')
    • element?: React.RefObject<View> - Element ref for position: 'element'
    • onIntersect?: () => void - Callback when intersection starts
    • onVisible?: () => void - Callback when intersection ends
    • onIntersectionChange?: (isIntersecting: boolean) => void - Callback for any intersection change

Returns:

  • isIntersecting: boolean - Current intersection state
  • ref: React.RefObject<T | null> - Ref for the scroll component. T defaults to ScrollView; pass a type argument to attach it to a list, e.g. useIntersectionObserver<FlatList<Item>>({ ... })
  • handleScroll: (event: any) => void - Scroll event handler
  • handleElementLayout: (event: any) => void - Layout handler for element positioning
  • reset: () => void - Reset intersection state

Convenience Hooks

useScrollToBottom(threshold?, options?)

Detects when the user has scrolled to the bottom of the scroll view.

const { isIntersecting, ref, handleScroll } = useScrollToBottom(50, {
  type: 'scrollview', // or 'flatlist', 'sectionlist'
  callbacks: {
    onIntersect: () => console.log('Bottom reached!'),
  },
});

useScrollToTop(threshold?, options?)

Detects when the user has scrolled to the top of the scroll view.

const { isIntersecting, ref, handleScroll } = useScrollToTop(50, {
  type: 'flatlist',
  callbacks: {
    onIntersect: () => console.log('Top reached!'),
  },
});

useScrollToCenter(threshold?, options?)

Detects when the user has scrolled to the center of the scroll view.

const { isIntersecting, ref, handleScroll } = useScrollToCenter(100, {
  type: 'sectionlist',
  callbacks: {
    onIntersect: () => console.log('Center reached!'),
  },
});

useElementIntersection(element, threshold?, callbacks?)

Detects when a specific element becomes visible in the scroll view.

const elementRef = useRef<View>(null);
const { isIntersecting, ref, handleScroll, handleElementLayout } =
  useElementIntersection(elementRef, 50, {
    onIntersect: () => console.log('Element is visible!'),
  });

return (
  <ScrollView ref={ref} onScroll={handleScroll}>
    <View ref={elementRef} onLayout={handleElementLayout}>
      <Text>This element will be tracked</Text>
    </View>
  </ScrollView>
);

💡 Common Use Cases

Infinite Scroll with FlatList

import React, { useState, useCallback } from 'react';
import { FlatList, View, Text, ActivityIndicator } from 'react-native';
import { useScrollToBottom } from '@raymardev/react-native-intersection-observer';

export default function InfiniteScrollList() {
  const [data, setData] = useState(
    Array.from({ length: 20 }, (_, i) => ({ id: i, text: `Item ${i}` }))
  );
  const [loading, setLoading] = useState(false);

  const loadMore = useCallback(async () => {
    if (loading) return;

    setLoading(true);
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 1000));

    const newData = Array.from({ length: 10 }, (_, i) => ({
      id: data.length + i,
      text: `Item ${data.length + i}`,
    }));

    setData(prev => [...prev, ...newData]);
    setLoading(false);
  }, [data.length, loading]);

  const { ref, handleScroll } = useScrollToBottom(100, {
    type: 'flatlist',
    callbacks: {
      onIntersect: loadMore,
    },
  });

  const renderItem = ({ item }: { item: any }) => (
    <View style={{ padding: 20, borderBottomWidth: 1 }}>
      <Text>{item.text}</Text>
    </View>
  );

  const renderFooter = () =>
    loading ? <ActivityIndicator style={{ padding: 20 }} /> : null;

  return (
    <FlatList
      ref={ref}
      data={data}
      renderItem={renderItem}
      onScroll={handleScroll}
      scrollEventThrottle={16}
      ListFooterComponent={renderFooter}
    />
  );
}

Pull-to-Refresh Detection

import React from 'react';
import { ScrollView, View, Text } from 'react-native';
import { useScrollToTop } from '@raymardev/react-native-intersection-observer';

export default function PullToRefresh() {
  const { isIntersecting, ref, handleScroll } = useScrollToTop(10, {
    type: 'scrollview',
    callbacks: {
      onIntersect: () => {
        console.log('Ready for pull-to-refresh!');
        // Trigger your refresh logic here
      },
    },
  });

  return (
    <ScrollView ref={ref} onScroll={handleScroll} scrollEventThrottle={16}>
      <View style={{ height: 2000 }}>
        <Text>Pull down to refresh</Text>
        {isIntersecting && <Text>🔄 Ready to refresh!</Text>}
      </View>
    </ScrollView>
  );
}

Element Visibility Tracking

import React, { useRef } from 'react';
import { ScrollView, View, Text } from 'react-native';
import { useElementIntersection } from '@raymardev/react-native-intersection-observer';

export default function ElementTracking() {
  const targetRef = useRef<View>(null);

  const { isIntersecting, ref, handleScroll, handleElementLayout } =
    useElementIntersection(targetRef, 50, {
      onIntersect: () => console.log('Target element is visible!'),
      onVisible: () => console.log('Target element is hidden!'),
    });

  return (
    <ScrollView ref={ref} onScroll={handleScroll} scrollEventThrottle={16}>
      <View style={{ height: 1000 }}>
        <Text>Scroll down to see the target element</Text>
      </View>

      <View
        ref={targetRef}
        onLayout={handleElementLayout}
        style={{
          height: 200,
          backgroundColor: isIntersecting ? '#4CAF50' : '#FF5722',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <Text style={{ color: 'white', fontSize: 18 }}>
          {isIntersecting ? 'I am visible! 🎉' : 'I am hidden 👻'}
        </Text>
      </View>

      <View style={{ height: 1000 }}>
        <Text>More content below</Text>
      </View>
    </ScrollView>
  );
}

🧭 How this relates to React Native's own APIs

React Native covers part of this ground already, and it is worth knowing which tool fits your case:

  • FlatList.onEndReached is the better choice for plain "load the next page" infinite scroll. It works with virtualization and needs no onScroll wiring.
  • onViewableItemsChanged + viewabilityConfig is the better choice for tracking which list items are visible, and it offers percentage thresholds and minimumViewTime, which this library does not.
  • IntersectionObserver landed natively in React Native 0.83, but only at the canary release level behind a feature flag — not in stable React Native, and not in Expo.

What this library adds is scroll-position detection — top, bottom, center and custom predicates over the scroll geometry — which the Intersection Observer model does not express, plus element tracking that works on stable React Native today. When the platform observer is present it can be used for element tracking via the strategy option; see the API reference.

Naming note: @react-navigation/native also exports a useScrollToTop, and it does the opposite of this one — it scrolls to the top, while this one detects the top. If you use both, import one under an alias.

📐 Element tracking accuracy

onLayout reports coordinates relative to the tracked view's parent, not to the scroll content, so those two only agree when the view is a direct child of the scroll view. useElementIntersection therefore measures the view against the scroll container itself and falls back to onLayout only when that is impossible.

For this to work, attach the hook's ref to your scroll component and pass the tracked view's ref as element. Nested elements are then measured correctly:

const targetRef = useRef<View>(null);
const { isIntersecting, ref, handleScroll, handleElementLayout } =
  useElementIntersection(targetRef, 50);

return (
  <ScrollView ref={ref} onScroll={handleScroll} scrollEventThrottle={16}>
    <View style={{ padding: 24 }}>
      {/* Nested as deeply as you like. */}
      <View ref={targetRef} onLayout={handleElementLayout} />
    </View>
  </ScrollView>
);

Call the returned measureElement() after any layout change the hook cannot observe on its own.

⚡ Performance Considerations

  • Use scrollEventThrottle={16} for smooth 60fps scroll events
  • The hook automatically optimizes re-renders by only updating state when intersection status changes
  • For large lists, consider using the convenience hooks which are optimized for specific use cases
  • Element intersection tracking requires layout measurements, so use sparingly for performance

🔧 TypeScript Support

This library is written in TypeScript and provides full type definitions. All hooks and their parameters are fully typed for the best development experience.

import {
  useIntersectionObserver,
  UseIntersectionObserverOptions,
  UseIntersectionObserverReturn,
} from '@raymardev/react-native-intersection-observer';

const options: UseIntersectionObserverOptions = {
  threshold: 50,
  position: 'bottom',
  onIntersect: () => console.log('Intersected!'),
};

const result: UseIntersectionObserverReturn = useIntersectionObserver(options);

👨‍💻 About the Author

This library is created and maintained by Ray Martin, a full-stack developer specializing in React Native, Next.js, and modern web technologies.

  • 🌐 Personal Website: raymartin.es (available in English and Spanish)
  • 💼 Professional Profile: Full-stack developer with expertise in enterprise-grade applications
  • 🚀 Open Source: Active contributor to the React Native ecosystem
  • 📧 Contact: Available for consulting and collaboration

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details on how to get started.

Quick Start for Contributors

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes and add tests
  4. Run tests: npm test
  5. Commit your changes: git commit -m 'feat: add amazing feature'
  6. Push to the branch: git push origin feature/amazing-feature
  7. Open a Pull Request

🐛 Issues & Support

📄 License

MIT © Ray Martin • GitHub

📈 Changelog

See CHANGELOG.md for a complete list of changes.

Recent Updates

  • v1.0.0 - Initial release with full feature set
  • Comprehensive documentation and examples
  • TypeScript support with full type definitions
  • Performance optimizations
  • Cross-platform compatibility

⭐ Star this repository if you find it helpful!


🌟 Connect with the Author

Ray Martin • Full-Stack Developer

Website GitHub NPM

Available for consulting and collaboration • Website available in English and Spanish

Made with ❤️ by Ray Martin