Fix React Native Modal ScrollView Not Scrolling (Best Modal Pattern)

Fix React Native Modal ScrollView Not Scrolling (Best Modal Pattern)



A common issue when building modals in React Native is that a ScrollView inside a Modal appears scrollable but does not actually scroll smoothly. The scrollbar may appear, but gestures don't move the content properly.

This usually happens because the modal overlay is intercepting touch events. If the overlay is implemented using TouchableOpacity or Pressable wrapped around the modal content, it can capture scroll gestures before they reach the ScrollView.

In this post, we'll look at the problem and the correct modal pattern used in production React Native apps.


The Problem

Many developers structure their modal like this:


<TouchableOpacity style={styles.overlay} onPress={onClose}>
  <TouchableOpacity style={styles.modal}>
    <ScrollView>
      ...
    </ScrollView>
  </TouchableOpacity>
</TouchableOpacity>

The outer TouchableOpacity captures gestures meant for the ScrollView. When the user tries to scroll, the overlay touch handler blocks the gesture.

Result: The scrollbar appears, but scrolling feels stuck or doesn't work.


The Correct Modal Pattern

The correct solution is to separate the overlay press area from the modal content instead of wrapping everything inside one touchable.

Use a background press layer to close the modal while keeping the modal content completely independent.


<Modal
  visible={visible}
  transparent
  animationType="fade"
  onRequestClose={onClose}
>
  <View style={styles.overlay}>

    {/* Background close layer */}
    <Pressable 
      style={StyleSheet.absoluteFill} 
      onPress={onClose} 
    />

    {/* Modal content */}
    <View style={styles.modal}>

      <View style={styles.header}>
        <Text>Modal Title</Text>
      </View>

      <ScrollView
        showsVerticalScrollIndicator
        nestedScrollEnabled
      >
        <Text>
          Long content goes here...
        </Text>
      </ScrollView>

      <View style={styles.footer}>
        <Text>Footer</Text>
      </View>

    </View>

  </View>
</Modal>

Recommended Styles


overlay: {
  flex: 1,
  backgroundColor: 'rgba(0,0,0,0.5)',
  justifyContent: 'center',
  alignItems: 'center'
},

modal: {
  width: '90%',
  maxHeight: '80%',
  backgroundColor: 'white',
  borderRadius: 12,
  overflow: 'hidden'
}

The key detail here is the maxHeight. Without it, the ScrollView might expand instead of scrolling.


Why This Pattern Works

Instead of nesting touchable elements like this:


TouchableOpacity (overlay)
   └ Modal Content
        └ ScrollView

We use a layered approach:


Overlay View
   ├ Pressable (background close)
   └ Modal Content
        └ ScrollView

This ensures the ScrollView receives gesture events directly, allowing smooth scrolling inside the modal.


Extra Scroll Improvements

You can also improve the scroll experience with:


<ScrollView
  showsVerticalScrollIndicator
  nestedScrollEnabled
  keyboardShouldPersistTaps="handled"
  bounces={true}
/>

These options help especially when the modal contains forms or dynamic content.


Final Thoughts

If your modal contains long text, lists, or dynamic content, using the correct overlay pattern is essential. Avoid wrapping your entire modal inside a touchable component, as it often leads to gesture conflicts.

By separating the background press area from the modal content, you ensure that scrolling works correctly while still allowing users to tap outside the modal to close it.

This simple pattern will save you hours of debugging when building scrollable modals in React Native.

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular posts from this blog

fixed: embedded-redis: Unable to run on macOS Sonoma

Copying MDC Context Map in Web Clients: A Comprehensive Guide

Reset user password for your own Ghost blog