Build Custom Scrolling Modals in React Native with Modalize

Master the art of creating scrollable, gesture-enabled modals that handle keyboard interactions gracefully

Why Modalize for Scrolling Modals?

Modals are an essential component of mobile app development. They provide a way to display content that overlays the main application interface, perfect for presenting additional information, forms, or actions without navigating away from the current screen.

While React Native provides a basic Modal component, developers often need more flexibility, particularly when dealing with scrolling content that needs to handle keyboard interactions gracefully. Modalize is specifically designed to address these pain points.

Modalize is described as "a highly customizable modal/bottom sheet that loves scrolling content" and is built with react-native-gesture-handler to address common issues with scrolling, swiping, and keyboard behaviors. For teams building React Native applications, Modalize provides the foundation for creating polished, professional modal experiences that users expect in modern mobile apps.

Key Features of Modalize

Everything you need to build professional modal experiences

Native Scrolling Support

Designed from the ground up to handle scrollable content without the complications of the built-in Modal component

Gesture-Based Interaction

Built with react-native-gesture-handler for smooth swipe-to-dismiss and drag interactions

Smart Keyboard Handling

Automatic keyboard avoidance and position adjustments for form inputs and text fields

Fully Customizable

Full control over appearance including handles, overlays, snap points, and animation behaviors

List Performance

Optimized FlatList integration for displaying large datasets within modals without performance issues

Bottom Sheet Mode

First-class support for bottom sheet patterns that users expect in modern mobile apps

Getting Started with Modalize

Installation and Dependencies

To use Modalize in your React Native project, install the package along with its peer dependency:

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

Modalize is built with react-native-gesture-handler to address common issues with scrolling, swiping, and keyboard behaviors. Make sure to properly configure react-native-gesture-handler in your project by following the official installation guide.

Basic Usage

The simplest implementation of Modalize involves wrapping your content and controlling visibility through state:

import { useRef } from 'react';
import { Modalize } from 'react-native-modalize';

const MyComponent = () => {
 const modalRef = useRef(null);

 const openModal = () => {
 modalRef.current?.open();
 };

 const closeModal = () => {
 modalRef.current?.close();
 };

 return (
 <>
 <Button title="Open Modal" onPress={openModal} />

 <Modalize ref={modalRef}>
 <View style={{ padding: 20 }}>
 <Text>Your modal content goes here</Text>
 </View>
 </Modalize>
 </>
 );
};

The ref-based API gives you programmatic control over the modal's state while maintaining clean component logic.

Core Props and Configuration

Modalize provides a rich set of props that control the modal's behavior and appearance. Understanding these props is key to building the exact modal experience you need.

Essential Props

Visibility and Height Control:

  • alwaysOpen - Sets a default open height when the modal is opened, useful for showing a peek of content
  • adjustToContentHeight - Automatically adjusts modal height to fit the content within

Content Rendering:

  • withScrollView - Wraps children in a ScrollView for scrolling content
  • children - The content to display within the modal
  • FlatListProps - Props for rendering lists within the modal with optimized performance
  • HeaderComponent - A fixed header that sits above the scrollable content
  • FooterComponent - A fixed footer that sits below the scrollable content

Keyboard Handling:

  • keyboardAvoidingBehavior - How the modal handles keyboard appearance (padding, position, or none)
  • keyboardAvoidingOffset - Offset for keyboard avoidance calculations

Customizing Appearance

The modal's visual appearance can be customized through:

<Modalize
 modalStyle={styles.modal}
 handleStyle={styles.handle}
 handlePosition="center"
 overlayStyle={styles.overlay}
 closeSnapBackStraight={true}
>
 {content}
</Modalize>
  • modalStyle - Custom styles for the modal container
  • handleStyle - Custom styles for the drag handle
  • handlePosition - Position of the handle ("center" or "outside")
  • overlayStyle - Custom styles for the overlay background
  • closeSnapBackStraight - Whether the modal closes with a straight snap animation

These customization options make Modalize an excellent choice for building consistent UI components across your React Native application.

Creating Scrolling Content Modals

The primary strength of Modalize is its excellent handling of scrolling content. Let's explore the different approaches for creating scrollable modals.

The withScrollView Prop

When your modal content exceeds the available screen height, enable scrolling with the withScrollView prop:

<Modalize
 withScrollView={true}
 showsVerticalScrollIndicator={true}
 scrollEnabled={true}
>
 <View>
 <Text>Header Content</Text>
 {/* Long content that needs scrolling */}
 <View style={{ height: 1000 }}>
 <Text>Scrollable content here</Text>
 </View>
 </View>
</Modalize>

Best Practices for Scrollable Content:

  1. Structure your content with a fixed header section followed by scrollable content
  2. Avoid mixing fixed and scrollable content without proper layout separation
  3. Use contentContainerStyle to customize the scroll view's inner layout

Handling Dynamic Content Height

When content height changes dynamically, combine alwaysOpen with adjustToContentHeight:

<Modalize
 alwaysOpen={100}
 adjustToContentHeight={true}
>
 {dynamicContent}
</Modalize>

Important Note: There is a known issue where ScrollView doesn't work correctly when combined with adjustToContentHeight. If you encounter this, consider using:

  1. A fixed alwaysOpen value instead
  2. FlatListProps for displaying large lists

FlatList Integration

For modals that display lists, use FlatListProps for optimal performance:

<Modalize
 flatListProps={{
 data: items,
 renderItem: ({ item }) => <ListItem item={item} />,
 keyExtractor: item => item.id,
 showsVerticalScrollIndicator: false,
 // Additional FlatList props...
 }}
/>

Using FlatListProps is preferred over wrapping content in ScrollView because it provides:

  • Virtualized rendering for better memory efficiency
  • Built-in scroll indicator configuration
  • Native refresh control integration
  • Optimized item recycling

Keyboard Handling and Best Practices

One of the most challenging aspects of modal development is handling the keyboard gracefully. Modalize provides built-in support for keyboard avoidance, but you may need to fine-tune the behavior for your specific use case.

Keyboard Avoidance Configuration

<Modalize
 keyboardAvoidingBehavior="padding"
 keyboardAvoidingOffset={100}
>
 {content}
</Modalize>

keyboardAvoidingBehavior Options:

  • "padding" - Adds padding to avoid the keyboard (recommended for most cases)
  • "position" - Changes the modal's position relative to the keyboard
  • "none" - Disables automatic keyboard avoidance

Form Implementation Patterns

When building forms within modals, follow these patterns for the best user experience:

<Modalize
 alwaysOpen={300}
 keyboardAvoidingBehavior="padding"
>
 <View style={{ padding: 16 }}>
 <Text style={{ fontSize: 18, fontWeight: 'bold' }}>Contact Form</Text>
 
 <TextInput
 placeholder="Your Name"
 style={{ height: 50, borderWidth: 1, marginTop: 16 }}
 onFocus={() => modalRef.current?.snapTo(1)}
 />
 
 <TextInput
 placeholder="Email Address"
 keyboardType="email-address"
 style={{ height: 50, borderWidth: 1, marginTop: 8 }}
 />
 
 <TextInput
 placeholder="Message"
 multiline
 numberOfLines={4}
 style={{ height: 100, borderWidth: 1, marginTop: 8 }}
 />
 
 <TouchableOpacity
 style={{ 
 backgroundColor: '#007AFF', 
 padding: 16, 
 borderRadius: 8,
 marginTop: 16 
 }}
 onPress={handleSubmit}
 >
 <Text style={{ color: 'white', textAlign: 'center' }}>Submit</Text>
 </TouchableOpacity>
 </View>
</Modalize>

Form Best Practices:

  1. Use alwaysOpen to prevent the modal from resizing during typing
  2. Position important form fields above the keyboard
  3. Handle focus events to adjust modal position if needed
  4. Consider using keyboardVerticalOffset for precise positioning on iOS

Advanced Customization

Modalize offers extensive customization options for creating unique modal experiences that match your app's design language.

Snap Points and Drag Behavior

Control how the modal responds to drag gestures:

<Modalize
 snapPoint={400}
 panGestureEnabled={true}
 panGestureComponentEnabled={true}
 closeSnapBackStraight={true}
>
 {content}
</Modalize>

Gesture Configuration Options:

  • snapPoint - The height the modal snaps to when opened
  • panGestureEnabled - Enable or disable pan gestures for closing
  • panGestureComponentEnabled - Enable or disable the drag handle component
  • closeSnapBackStraight - Whether the modal closes with a straight snap animation

Custom Header and Footer

Add fixed headers and footers to your modal:

<Modalize
 HeaderComponent={
 <View style={{ padding: 20, borderBottomWidth: 1 }}>
 <Text style={{ fontSize: 20, fontWeight: 'bold' }}>Modal Title</Text>
 </View>
 }
 FooterComponent={
 <View style={{ padding: 20, borderTopWidth: 1 }}>
 <Button title="Done" onPress={closeModal} />
 </View>
 }
>
 {scrollableContent}
</Modalize>

Complete Example: Profile Modal

import { useRef } from 'react';
import { Modalize } from 'react-native-modalize';
import { Image, View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native';

const ProfileModal = ({ visible, onClose }) => {
 const modalRef = useRef(null);

 React.useEffect(() => {
 if (visible) {
 modalRef.current?.open();
 } else {
 modalRef.current?.close();
 }
 }, [visible]);

 return (
 <Modalize
 ref={modalRef}
 onClosed={onClose}
 alwaysOpen={400}
 modalStyle={styles.modal}
 handleStyle={styles.handle}
 keyboardAvoidingBehavior="padding"
 >
 <View style={styles.content}>
 <View style={styles.avatarContainer}>
 <Image 
 source={{ uri: 'https://example.com/avatar.jpg' }}
 style={styles.avatar}
 />
 <TouchableOpacity style={styles.changePhotoButton}>
 <Text style={styles.changePhotoText}>Change Photo</Text>
 </TouchableOpacity>
 </View>
 
 <TextInput
 placeholder="Full Name"
 style={styles.input}
 defaultValue="John Doe"
 />
 
 <TextInput
 placeholder="Email"
 keyboardType="email-address"
 style={styles.input}
 defaultValue="[email protected]"
 />
 
 <TextInput
 placeholder="Bio"
 multiline
 numberOfLines={3}
 style={{...styles.input, height: 80}}
 />
 </View>
 </Modalize>
 );
};

const styles = StyleSheet.create({
 modal: {
 backgroundColor: '#fff',
 borderTopLeftRadius: 20,
 borderTopRightRadius: 20,
 },
 handle: {
 backgroundColor: '#ccc',
 width: 40,
 height: 4,
 borderRadius: 2,
 },
 content: {
 padding: 20,
 },
 avatarContainer: {
 alignItems: 'center',
 marginBottom: 20,
 },
 avatar: {
 width: 80,
 height: 80,
 borderRadius: 40,
 marginBottom: 10,
 },
 input: {
 height: 50,
 borderWidth: 1,
 borderColor: '#ddd',
 borderRadius: 8,
 paddingHorizontal: 15,
 marginBottom: 12,
 },
});

Common Issues and Solutions

When working with Modalize, you may encounter some common challenges. Here are solutions to the most frequently reported issues.

ScrollView Not Working with adjustToContentHeight

There is a known issue where ScrollView doesn't work correctly when combined with adjustToContentHeight. The modal may not scroll even when content overflows.

Solutions:

  1. Use alwaysOpen with a fixed height value instead
  2. Use FlatListProps instead of wrapping content in ScrollView
  3. Force re-render by adding a key to the children when content changes

Cannot Scroll FlatList Inside Modalize

If your FlatList inside Modalize isn't scrolling properly:

// Problem: FlatList wrapped in children
<Modalize>
 <FlatList
 data={items}
 renderItem={renderItem}
 />
</Modalize>

// Solution: Use flatListProps instead
<Modalize
 flatListProps={{
 data: items,
 renderItem: renderItem,
 keyExtractor: item => item.id,
 showsVerticalScrollIndicator: true,
 scrollEnabled: true,
 }}
/>

Height Calculation Issues

When using alwaysOpen with adjustToContentHeight, the modal may not expand properly if the content height changes dynamically.

Solution: Use a key to force re-render when content changes:

<Modalize
 alwaysOpen={100}
 adjustToContentHeight={true}
>
 <View key={contentVersion}>
 {dynamicContent}
 </View>
</Modalize>

Keyboard Covers Form Fields

If the keyboard still covers form fields despite keyboard avoidance:

<Modalize
 keyboardAvoidingBehavior="padding"
 keyboardAvoidingOffset={Platform.OS === 'ios' ? 120 : 100}
>
 <ScrollView keyboardShouldPersistTaps="handled">
 {formContent}
 </ScrollView>
</Modalize>

Modal Doesn't Close on Background Tap

Ensure the overlay is configured correctly:

<Modalize
 overlay={true}
 overlayStyle={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}
 onOverlayPress={closeModal}
>
 {content}
</Modalize>
Modalize vs React Native Built-in Modal
FeatureReact Native ModalModalize
Scrolling ContentLimited, requires workaroundsNative support designed from the ground up
Gesture HandlingBasic tap to dismissAdvanced swipe, drag, and pan gestures
Keyboard AvoidanceManual implementation requiredAutomatic with configurable behavior
CustomizationLimited to basic stylingFull control over appearance and behavior
Bottom Sheet ModeNot supportedFirst-class support with snap points
PerformanceNative implementationOptimized for lists with FlatListProps
Animation OptionsBasic fade/slideRich animation configuration

Conclusion

Modalize provides a robust solution for creating custom scrolling modals in React Native. Its focus on scrollable content, combined with excellent gesture support and keyboard handling, makes it an excellent choice for displaying:

  • Forms - Login, registration, contact forms with keyboard handling
  • Lists - Selection lists, settings panels, search results
  • Detail Views - Product details, user profiles, content previews
  • Actions Sheets - Confirmation dialogs, option selections

Key Takeaways:

  1. Use withScrollView for general scrolling content needs
  2. Prefer FlatListProps for displaying large lists within modals
  3. Configure alwaysOpen to show content preview when the modal first appears
  4. Use keyboardAvoidingBehavior to ensure form fields remain accessible
  5. Customize appearance through modalStyle, handleStyle, and overlayStyle

When choosing Modalize for your project, consider your specific needs:

  • For simple modals with minimal scrolling, the built-in React Native Modal may suffice
  • For bottom sheet patterns and advanced gestures, Modalize is an excellent choice
  • For maximum customization and control, Modalize provides the flexibility you need

By following the patterns and best practices outlined in this guide, you can create professional, accessible modal experiences that delight your users and enhance your app's usability. Our web development team specializes in building React Native applications with sophisticated UI components like Modalize that elevate the user experience.

Frequently Asked Questions

Ready to Build Better Mobile Experiences?

Our team specializes in creating custom mobile applications with React Native, including sophisticated UI components like scrolling modals and bottom sheets.