Skip to content

馃敟[馃悰] Issues with chat overlays in modals聽#3860

Closed

Description

@zbarbuto

Issue

Assume we have everything configured in our app to show a chat:

Image

Regular chat in our root view - so good so far

For various reasons, we might want to have a text chat inside a modal in some instances in our app. Assume react-navigation as presentation: 'fullScreenModal'. However, if we do this, the long-press overlay no longer works. The message becomes invisible and the overlay does not show:

Image

I have long-pressed the "Test" message here, note there's no black overlay or reactions and the message itself is gone

The only partial fix I've found is to provide the modal its own <OverlayProvider />. This goes against the docs, but stay with me for a sec:

<Stack.Screen
  name="ChatModal"
  component={ChatModalScreen}
  layout={({ children }) => (
     // Give the modal its own overlay provider so the long-press can sit on top
    <OverlayProvider>{children}</OverlayProvider>
  )}
  options={{
    presentation: 'fullScreenModal',
    title: 'Modal chat',
  }}
/>

This does allow the reactions to show in the modal view (with a couple of caveats):

Image

We have our overlay inside the modal now (albeit with some issues with the header)

However, now if we close the modal, the overlays are now broken for our original chat: long-pressing a message shows an overlay with a misaligned reply overlay and no reaction bar:

Image

Navigating back to our root view, the overlays no longer layer properly here

Image

If you look closely, the reaction picker is actually behind the message actions

Steps to reproduce

Steps to reproduce the behavior:

  1. Run a simple app that includes a text chat in the root view and a react-navigation full screen modal that has a chat (example in customization below but can also provide a repro repo if it's useful).
  2. Open the modal view
  3. long-press a message in the chat to open reactions/message actions in the modal view.

Expected behavior

Expect that with a single root <OverlayProvider /> that the long-press actions would still work when the chat is in a modal - or at least have a way to allow these to function in a modal.

Project Related Information

Customization

Click To Expand

Basic app

// Main app
    <SafeAreaProvider>
      <GestureHandlerRootView style={styles.container}>
        <ChatWrapper>
          <NavigationContainer>
            <Stack.Navigator>
              <Stack.Screen
                name="Chat"
                component={ChatScreen}
                options={{ title: 'Chat' }}
              />
              <Stack.Screen
                name="ChatModal"
                component={ChatModalScreen}
                 // Comment as necessary, this fixes one issue but causes another
                 // layout={({ children }) => (
                 // <OverlayProvider>{children}</OverlayProvider>
                 // )}
                options={{
                  presentation: 'fullScreenModal',
                  title: 'Modal chat',
                }}
              />
            </Stack.Navigator>
          </NavigationContainer>
        </ChatWrapper>
        <StatusBar style="auto" />
      </GestureHandlerRootView>
    </SafeAreaProvider>

// Chat Screen
    <View style={styles.container}>
      <Button title="Open modal" onPress={() => navigation.navigate('ChatModal')} />
      <StreamTextChat channelId={mainChannelId} />
    </View>

// ChatModalScreen
export function ChatModalScreen({ navigation }: ChatModalScreenProps) {
  return (
    <View style={styles.container}>
      <Button title="Close" onPress={() => navigation.goBack()} />
      <StreamTextChat channelId={modalChannelId} />
    </View>
  );
}

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

StreamTextChat is basically vanilla from the getting started tutorial i.e.:

    <Channel
      bottomInset={bottom}
      channel={channel}
    >
      <MessageList />
      <MessageComposer />
    </Channel>

Offline support

  • The feature I'm having does not occur when offline support is disabled. (stripe out if not applicable)

Environment

Click To Expand

package.json:

{
  "name": "sandbox",
  "version": "1.0.0",
  "main": "index.ts",
  "dependencies": {
    "@react-native-community/netinfo": "12.0.1",
    "@react-navigation/elements": "^2.9.44",
    "@react-navigation/native": "^7.5.0",
    "@react-navigation/native-stack": "^7.20.0",
    "expo": "~57.0.28",
    "expo-image-manipulator": "~57.0.21",
    "expo-status-bar": "~57.0.1",
    "react": "19.2.3",
    "react-native": "0.86.3",
    "react-native-gesture-handler": "~2.32.0",
    "react-native-reanimated": "4.5.1",
    "react-native-safe-area-context": "~5.7.0",
    "react-native-screens": "~4.26.0",
    "react-native-svg": "15.15.4",
    "react-native-teleport": "^1.2.2",
    "react-native-worklets": "0.10.1",
    "stream-chat-expo": "^9.9.4"
  },
  "devDependencies": {
    "@types/react": "~19.2.2",
    "typescript": "~6.0.3"
  },
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "private": true
  }
}
  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • stream-chat-react-native version you're using that has this issue:
    • "stream-chat-expo": "^9.9.4"
  • Device/Emulator info:
    • iPhone 17 iOS 26.2

Additional context

Can provide a full reproduction repo if required. I haven't tried this with expo navigation, only react-navigation.

Screenshots

Click To Expand

See the main issue body


Activity

  1. isekovanic commented on Oct 15, 2026

    @isekovanic
    Contributor

    Hi @zbarbuto ,

    The problem is something relatively different here. Specifically, this blogpost is something you might want to read with regards to it.

    Modals simply need to be self contained and carry their own OverlayProvider (and conversely their own variety of higher order views that come with our main chat providers), similar to how they'd need to carry their own GestureHandlerRootView for RNGH integration for example.

    The main problem with having 2 OverlayProviders is the fact that the state layer is shared (and so are the portal hosts that relate to them directly). This is kind of breaking to fix (in V9 at least) and there are also a bunch of other implications that are sort of hard to work around. However in these types of specific scenarios it should be relatively fine to use.

    I'll look into the bug you're having with the provider underneath though, that might be related to unmounting hosts or something. That's my current theory anyway.

    I'll keep you posted if I'm able to reproduce it.

  2. zbarbuto commented on Oct 15, 2026

    @zbarbuto
    ContributorAuthor

    Thanks for the info and yeah, I thought it might be related to that as I've come across the issue before using Teleport and I know stream did recently start using teleport in a recent major version. It's sort of why I tried adding the OverlayProvider to the modal to combat it. But yes I could see hints that it's probably not the right solution.

    Even if not allowing multiple OverlayProviders, having some other export to wrap modals to support allowing chats in both regular stack pages and modals would be extremely useful.

  3. isekovanic commented on Oct 15, 2026

    @isekovanic
    Contributor

    Yep, I have a fix ready I'm just testing stuff out. I'll update the docs as well so that this part is covered well once that's done.

    I'll keep you posted !

  4. zbarbuto commented on Oct 15, 2026

    @zbarbuto
    ContributorAuthor

    Legend! Thanks so much for getting to this so quickly.

  5. isekovanic commented on Oct 16, 2026

    @isekovanic
    Contributor

    Hi again @zbarbuto,

    The fix is merged in #3862 and is available in 9.10.1-beta.1, so you can already test it by bumping the version.

    With it, multiple OverlayProviders can be mounted at the same time, so the root chat keeps working after the modal closes. You don't need any special export for this, a regular OverlayProvider inside the modal is enough.

    One thing to change on your end though, instead of wrapping the screen through layout, wrap the modal's own navigator, the same way the root OverlayProvider wraps your main stack. With layout the native header still sits above the provider, so the overlay can't cover it and ends up offset by the header's height (that was the header thingy you mentioned). So something like:

    const ModalStack = createNativeStackNavigator();
    
    const ChatModal = () => (
      <OverlayProvider value={{ style: theme }} i18nInstance={streami18n}>
        <ModalStack.Navigator>
          <ModalStack.Screen name='ModalChat' component={ChatModalScreen} options={{ title: 'Modal chat' }} />
        </ModalStack.Navigator>
      </OverlayProvider>
    );
    
    <Stack.Screen
      name='ChatModal'
      component={ChatModal}
      options={{ headerShown: false, presentation: 'fullScreenModal' }}
    />

    A couple of things to keep in mind:

    • Pass the nested OverlayProvider the same props as your root one (theme, i18nInstance, accessibility), otherwise everything inside the modal falls back to the defaults
    • Android doesn't strictly need the extra provider since the modal lives in the same window there, but having it doesn't hurt on either platform, so you can keep the same setup for both
    • fullScreenModal works best. Sheet presentations like pageSheet and formSheet don't start at the top of the screen, so the overlay will be slightly offset there
    • Keep it to one OverlayProvider at the root plus one per modal. Having them side by side (for example one per tab) isn't supported

    I'm also adding a guide on this to the docs. Let me know how the beta works out for you ! Once I'm done with the docs I'll follow up here again once this has been released.

  6. isekovanic commented on Oct 19, 2026

    @isekovanic
    Contributor

    This has now been released here as well !

    Here is the cookbook I promised as well, in case someone finds it useful: https://getstream.io/chat/docs/sdk/react-native/guides/chat-in-modals/

  7. zbarbuto commented on Oct 22, 2026

    @zbarbuto
    ContributorAuthor

    Can confirm this new release has fixed my issues 馃憤

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions