Rupesh Chaudhari Rupesh is a programming enthusiast who is currently working with React Native to build beautiful hybrid mobile apps. He likes to solve competitive programming puzzles and enjoys gaming in his free time.

Build custom scrolling modals in React Native with Modalize

6 min read 1876 101

Build Custom Scrolling Modals in React Native with Modalize

Modals are an essential component of mobile app development, allowing developers to present information or actions unobtrusively. In React Native, modals are typically used to show additional content without navigating to a new screen.

While React Native provides a basic Modal component, using it to create custom and scrolling modals with a seamless user experience can be a bit challenging. Thankfully, the react-native-modalize library (aka Modalize) offers an elegant and flexible solution to build highly customizable scrolling modals in React Native.

In this article, we will explore how we can easily create custom scrolling content modals in React Native with Modalize. You can find the complete code used in this tutorial on GitHub.

Jump ahead:

Setting up react-native-modalize

Modalize is a popular open source library. It provides an advanced modal component that offers smooth and natural scrolling behavior, making it ideal for displaying a wide range of content, from simple text to complex forms or lists. The library is built on top of React Native’s Animated API, ensuring smooth animations and transitions that will impress your users.

To start off, we’ll create a new React Native project by running the command below in the terminal:

npx react-native init <project-name-here>

Next, install Modalize using npm or Yarn:

npm install react-native-modalize
# or
yarn add react-native-modalize

This library has a dependency on React Native Gesture Handler, so we will go ahead and install it in our project:

yarn add react-native-gesture-handler
# or
npm install react-native-gesture-handler

For Gesture Handler versions ≥2.0.0, we need to follow these steps to make it work:

  1. Wrap your app entry point with the GestureHandlerRootView component
  2. Import react-native-gesture-handler at the top of your index.js file

With the above changes, your index.js and App.tsx files will look like the below:

// index.js

// Add this 👇
import 'react-native-gesture-handler';

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
// App.tsx
import {SafeAreaView, StyleSheet, Text} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
const App = () => {
  return (
    <GestureHandlerRootView style={styles.container}>
      <SafeAreaView style={styles.container}>
        <Text>{'Open Modal'}</Text>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
export default App;

With Gesture Handler installed, we can now dive into creating custom scrolling modals that will elevate the user experience in our React Native apps.

Using react-native-modalize for custom modals

Let’s start by implementing a basic example to understand how to use Modalize effectively. To that end, we’ll create a simple modal that displays some text.

Add the below code in your App.tsx file:

import {useRef} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {Modalize} from 'react-native-modalize';

const App = () => {
  const modalRef = useRef<Modalize>(null);
  const openModal = () => modalRef?.current?.open();
  return (
    <GestureHandlerRootView style={styles.container}>
      <SafeAreaView style={styles.container}>
        <Text onPress={openModal}>{'Open Modal'}</Text>
        <Modalize ref={modalRef}>
          <View style={{padding: 20}}>
            <Text style={{fontSize: 22, fontWeight: 'bold', lineHeight: 34}}>
              {'This is a modal'}
            </Text>
            <Text>
              {
                'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et euismod nisl. Nulla facilisi. Aenean et mi volutpat, iaculis libero non, luctus quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur euismod dapibus metus, eget egestas quam ullamcorper eu.'
              }
            </Text>
          </View>
        </Modalize>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ddd',
  },
});
export default App;

Here, we created a ref for our modal named modalRef and assigned it to our Modalize component. The Modalize component wraps the content we want to display inside the modal. The ref attribute allows us to access the modal’s methods, such as opening and closing it.

The above code gives us the following output:

A Basic Implementation of a Modal Using react-native-modalize

N.B., the previews here will show our app running on iOS, but you can view the same previews on an Android device in the GitHub repo.

Handling scrollable content

One of the significant advantages of using Modalize is its high level of customization. You can tailor the modal to match your app’s design and user experience requirements.

By default, the content inside any modal built with react-native-modalize is automatically scrollable if it exceeds the modal’s height. This allows you to present lengthy information without worrying about content overflow.

Let’s take a look at what happens if we have content larger than the modal height. Modify the code in the Modalize component like so:

<Modalize
  ref={modalRef}
  modalHeight={200}
  scrollViewProps={{showsVerticalScrollIndicator: false}}>
  <View style={{padding: 20}}>
    <Text style={{fontSize: 22, fontWeight: 'bold', lineHeight: 34}}>
      {'This is a modal'}
    </Text>
    <Text>
      {
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et euismod nisl. Nulla facilisi. Aenean et mi volutpat, iaculis libero non, luctus quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur euismod dapibus metus, eget egestas quam ullamcorper eu.'
      }
      {'\n\n'}
      {
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et euismod nisl. Nulla facilisi. Aenean et mi volutpat, iaculis libero non, luctus quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur euismod dapibus metus, eget egestas quam ullamcorper eu.'
      }
    </Text>
  </View>
</Modalize>

We have explicitly set our modal’s height to 200 and used scrollViewProps to hide the scroll indicator. Modalize will automatically wrap the content inside a ScrollView. Let’s see how our modal looks now:

Example of a Scrolling Modal Implemented with react-native-modalize

Using snap points

Snap points refer to specific positions that the modal can animate to when opening or closing. Instead of smoothly transitioning to its full height immediately, the modal can stop at predefined heights during its opening or closing animation. This behavior allows you to present the modal in different states or sizes, enhancing the user experience and providing a more dynamic interaction.

Many third-party modal libraries — Modalize included — offer a snapPoint prop that allows you to define these snap points. When the modal is opened, it will smoothly animate to the specified snap point; when closed, it will animate back to either its starting position or another specified snap point.

Update your Modalize declaration using the code below to see how we can use snap points:

<Modalize
  ref={modalRef}
  modalHeight={600}
  snapPoint={300}
  closeSnapPointStraightEnabled={false}
  scrollViewProps={{showsVerticalScrollIndicator: false}}>
  <View style={{padding: 20}}>
    <Text style={{fontSize: 22, fontWeight: 'bold', lineHeight: 34}}>
      {'This is a modal'}
    </Text>
    <Text>
      {
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et euismod nisl. Nulla facilisi. Aenean et mi volutpat, iaculis libero non, luctus quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur euismod dapibus metus, eget egestas quam ullamcorper eu.'
      }
      {'\n\n'}
      {
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et euismod nisl. Nulla facilisi. Aenean et mi volutpat, iaculis libero non, luctus quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur euismod dapibus metus, eget egestas quam ullamcorper eu.'
      }
    </Text>
  </View>
</Modalize>

In this example, the snapPoint prop is set to 300. This means that when the modal is opened, it will smoothly animate to a height of 300 units. When the modal is closed, it will animate back to its starting position.

closeSnapPointStraightEnabled indicates whether the modal should be stopped at the snap point when closing. When set to false, the modal stops at the snap point before closing, and the opposite when set to true. By default, its value is true.



With our above code changes, our modal now looks like this:

Example of a Modal That Uses a Snap Point Built with react-native-modalize

Rendering a FlatList

Modalize uses ScrollView as its default renderer, but it also allows us to use FlatList or SectionList as renderers. Let’s see how we can use FlatList as a renderer for our Modalize component.

Let’s add the below code to our App.tsx:

// <imports here>

const DATA = [
  {
    id: '1',
    title: 'First Item',
    imgUrl: 'https://picsum.photos/800',
  },
  // Other 9 Items like this here
];

const App = () => {
  const flatlistModalRef = useRef<Modalize>(null);
  const openFlatListModal = () => flatlistModalRef?.current?.open();

  const renderItem = ({item}) => (
    <View
      style={{
        flexDirection: 'row',
        alignItems: 'center',
        paddingVertical: 16,
      }}>
      <Image
        source={{uri: item?.imgUrl}}
        style={{
          height: 100,
          width: 100,
          borderRadius: 20,
          marginHorizontal: 16,
        }}
      />
      <Text style={{fontWeight: '700', fontSize: 24, letterSpacing: 1.4}}>
        {item?.title}
      </Text>
    </View>
  );

  return (
    <GestureHandlerRootView style={styles.container}>
      <SafeAreaView style={styles.container}>
        <Text onPress={openFlatListModal}>{'Open FlatList Modal'}</Text>
        <Modalize
          ref={flatlistModalRef}
          modalTopOffset={80}
          flatListProps={{
            data: DATA,
            keyExtractor: item => item.id,
            showsVerticalScrollIndicator: false,
            renderItem,
          }}
        />
      </SafeAreaView>
    </GestureHandlerRootView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ddd',
  },
});

export default App;

Above, we used Modalize’s flatListProps to initialize our FlatList inside the modal. These are the standard props that we pass into FlatList while creating a List.

The above code creates the below modal list:

Example of a Modal Using FlatList as Its Renderer

Creating an always-open modal

We can use the alwaysOpen prop in react-native-modalize to control the modal’s behavior, ensuring that it remains open and visible on the screen. When set to any number value, the modal will not be closable, and it will remain displayed at the given height with no way for users to close it.

Let’s add the following prop to our FlatList modal:

<Modalize
  ref={flatlistModalRef}
  modalTopOffset={80}
 // Add this 👇
  alwaysOpen={140}
  flatListProps={{
    data: DATA,
    keyExtractor: item => item.id,
    showsVerticalScrollIndicator: false,
    renderItem,
  }}
/>

This will make our modal always open with a height of 140 units:

Example of an Always-Open Modal Built with Modalize

You might use the alwaysOpen prop in scenarios where you want to present essential information or instructions that should always remain visible. For example, you could use it to display an important announcement or a tutorial that users need to read before interacting with the rest of the app.

Because alwaysOpen prevents the user from closing the modal under any circumstances, it’s essential to use it thoughtfully. Doing otherwise could seriously impact your app’s user experience.

If the information inside the modal is crucial for the user’s interaction, it might be a suitable choice. But for content that the user might reasonably want to dismiss, consider using the modal’s default behavior or providing a close button.

Conclusion

Creating custom, scrolling modals in React Native is simple with the react-native-modalize library. Its flexibility and numerous customization options make it an excellent choice for developers who want to deliver an outstanding user experience.

We explored the basics of using Modalize and learned how to implement a custom modal with scrolling content. We also touched upon some of the essential customization options available in the library, such as using snap points, rendering a FlatList in the modal, and configuring always-open modals. Again, you can find the full code we used in this tutorial in the GitHub repo here.

As you continue to build React Native applications, consider incorporating Modalize to create visually appealing and interactive modals that enhance user engagement and interaction. Thank you for reading!

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — try LogRocket for free.

Rupesh Chaudhari Rupesh is a programming enthusiast who is currently working with React Native to build beautiful hybrid mobile apps. He likes to solve competitive programming puzzles and enjoys gaming in his free time.

Leave a Reply