react-native-scroll-into-view
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.1 • Public • Published

    react-native-scroll-into-view

    NPM NPM Build Status Twitter Follow

    Scroll a ReactNative View ref into the visible portion of a ScrollView.

    Similar to DOMElement.scrollIntoView() for web, with some extras.

    yarn add react-native-scroll-into-view
    // or
    npm install react-native-scroll-into-view --save
    

    There is no native code: this library is compatible with Expo managed workflow.

    expo

    Why ?

    On long scrollable forms, can ensure errors become visible to the user on submit:

    Formik example

    Building some kind of "sections index":

    Sections example

    But really you are free to build whatever you want with it

    Features:

    • Declarative component API
    • Imperative hook API
    • Configurable at many levels
    • Different alignment modes
    • Insets
    • Typescript definitions
    • Support for composition/refs/other ScrollView wrappers (Animated.ScrollView, react-native-keyboard-aware-scroll-view, glamorous-native...)

    Note we don't plan to support anything else than ScrollView. Virtualized lists generally offer methods to scroll to a given index.

    Minimal hooks example

    import { View, Text, ScrollView } from 'react-native';
    import {
      wrapScrollView,
      useScrollIntoView,
    } from 'react-native-scroll-into-view';
    
    const CustomScrollView = wrapScrollView(ScrollView);
    
    function MyScreen() {
      return (
        <CustomScrollView>
          <MyScreenContent />
        </CustomScrollView>
      );
    }
    
    function MyScreenContent() {
      const scrollIntoView = useScrollIntoView();
      const viewRef = useRef();
      return (
        <>
          <Button onPress={() => scrollIntoView(viewRef.current)}>
            Scroll a view ref into view
          </Button>
    
          <View style={{ height: 100000 }}>
            <Text>Some long ScrollView content</Text>
          </View>
    
          <View ref={viewRef}>
            <Text>Will be scrolled into view on button press</Text>
          </View>
        </>
      );
    }

    API

    import {
      ScrollIntoView, // enhanced View container
      wrapScrollView, // simple wrapper, no config
      wrapScrollViewConfigured, // complex wrapper, takes a config
      useScrollIntoView, // access hook for imperative usage
    } from 'react-native-scroll-into-view';
    
    // Available options with their default value
    const options = {
      // auto: ensure element appears fully inside the view (if not already inside). It may align to top or bottom.
      // top: align element to top
      // bottom: align element to bottom
      // center: align element at the center of the view
      align: 'auto',
    
      // Animate the scrollIntoView() operation
      animated: true,
    
      // By default, scrollIntoView() calls are throttled a bit because it does not make much sense
      // to scrollIntoView() 2 elements at the same time (and sometimes even impossible)
      immediate: false,
    
      // Permit to add top/bottom insets so that element scrolled into view
      // is not touching directly the borders of the scrollview (like a padding)
      insets: {
        top: 0,
        bottom: 0,
      },
    
      // Advanced: use these options as escape hatches if the lib default functions do not satisfy your needs
      computeScrollY: (scrollViewLayout, viewLayout, scrollY, insets, align) => {},
      measureElement: viewRef => {},
    };
    
    // Wrap the original ScrollView
    const CustomScrollView = wrapScrollView(ScrollView);
    
    // Use the wrapped CustomScrollView as a replacement of ScrollView
    function MyScreen() {
      return (
        <CustomScrollView
          // Can provide default options (overrideable)
          scrollIntoViewOptions={scrollIntoViewOptions}
        >
          <ScreenContent />
        </CustomScrollView>
      );
    }
    
    // Implement ScreenContent (inner of the ScrollView) with the useScrollIntoView and refs
    function ScreenContent() {
      const scrollIntoView = useScrollIntoView();
      const viewRef = useRef();
    
      return (
        <>
          <Button
            onPress={() => {
              scrollIntoView(viewRef.current, options);
            }}
          >
            Scroll a view ref into view
          </Button>
    
          <View style={{ height: 100000 }}>
            <Text>Some long ScrollView content</Text>
          </View>
    
          <View ref={viewRef}>
            <Text>Will be scrolled into view on button press</Text>
          </View>
        </>
      );
    }
    
    // Or implement ScreenContent (inner of the ScrollView) with class + declarative ScrollIntoView component
    class ScreenContent extends React.Component {
      render() {
        return (
          <>
            <ScrollIntoView>
              <Text>This will scroll into view on mount</Text>
            </ScrollIntoView>
    
            <ScrollIntoView align="center">
              <Text>This will scroll into view on mount and will be centered</Text>
            </ScrollIntoView>
    
            <ScrollIntoView animated={false}>
              <Text>This will scroll into view on mount without any animation</Text>
            </ScrollIntoView>
    
            <ScrollIntoView immediate={true}>
              <Text>
                This will not throttle scrollIntoView calls, as by default it does
                not make much sense to scroll into view multiple elements at the
                same time...
              </Text>
            </ScrollIntoView>
    
            <ScrollIntoView enabled={false}>
              <Text>This will scroll into view whenever enabled becomes true</Text>
            </ScrollIntoView>
    
            <ScrollIntoView scrollIntoViewKey="some string">
              <Text>
                This will scroll into view whenever scrollIntoViewKey changes
              </Text>
            </ScrollIntoView>
    
            <ScrollIntoView
              onMount={false}
              onUpdate={true}
              scrollIntoViewKey="some string"
            >
              <Text>
                This will scroll into on update (if it becomes enabled, or key
                changes)
              </Text>
            </ScrollIntoView>
    
            <ScrollIntoView scrollIntoViewOptions={options}>
              <Text>
                This will scroll into view on mount with custom option props
              </Text>
            </ScrollIntoView>
    
            <View>
              <ScrollIntoView
                enabled={false}
                ref={ref => (this.scrollIntoViewRef = ref)}
              >
                <Text>This will scroll into view when the button is pressed</Text>
              </ScrollIntoView>
              <Button
                title="Make above text scroll into view with custom options"
                onPress={() =>
                  this.scrollIntoViewRef.scrollIntoView(scrollIntoViewOptions)
                }
              />
            </View>
          </>
        );
      }
    }

    You can also configure the HOC:

    const CustomScrollView = wrapScrollViewConfigured({
      // SIMPLE CONFIG:
      // ScrollIntoView default/fallback options
      options: scrollIntoViewOptions,
    
      // ADVANCED CONFIG:
      // Use this if you use a ScrollView wrapper that does not use React.forwardRef()
      refPropName: 'ref',
      // unwraps the ref that the wrapped ScrollView gives you (this lib need the bare metal ScrollView ref)
      getScrollViewNode: ref => ref,
      // fallback value for throttling, can be overriden by user with props
      scrollEventThrottle: 16,
    })(ScrollView);

    All these hoc configurations can also be provided to the CustomScrollView as props.

    Demos:

    You can run the example folder as an Expo app with yarn start

    It is also published on Expo

    Basic example

    Basic insets example

    Scroll to next example

    Sections example

    Formik example

    Recipes

    Using in forms:

    The integration with form libraries like Formik and Redux-form is very simple (see Formik example)

    Formik integration

    • By default, the first error field of the form will reveal itself
    • enabled={!!error} means we'll only scroll into view fields that have an error
    • scrollIntoViewKey={submitCount} means we'll scroll into view fields which still have errors on every Formik submit attempt (submitCount is provided by Formik)

    Using with react-native-keyboard-aware-scroll-view

    KeyboardAwareScrollView does not forward refs by default so we need to obtain ref by using the innerRef prop:

    const ScrollIntoViewScrollView = wrapScrollViewConfigured({
      refPropName: 'innerRef',
    })(KeyboardAwareScrollView);
    

    TODOs:

    • Tests
    • Universal/Web support
    • Support horizontal ScrollView?

    Contribute

    If your changes are impactful, please open an issue first.

    License

    MIT

    Some code is inspired from contribution of @sebasgarcep of an initial scrollIntoView support for react-native-keyboard-aware-scroll-view

    Hire a freelance expert

    Looking for a React/ReactNative freelance expert with more than 5 years production experience? Contact me from my website or with Twitter.

    Install

    npm i react-native-scroll-into-view

    DownloadsWeekly Downloads

    2,398

    Version

    2.0.1

    License

    MIT

    Unpacked Size

    52.6 kB

    Total Files

    32

    Last publish

    Collaborators

    • slorber