Skip to content

popToNative closes all React Native screens #354

@iAndrew25

Description

@iAndrew25

Hello,

I've been using react-native-brownfield for a while now and recently we added a new module inside the RNApp project.
A module is a separate stack which comes with its own screens and logic. Normally these modules should be as isolated as possible one from another.
The way I implemented the module is by using the NavigationContainer and the Stack.Navigator from react-navigation.

For testing purposes I've made a new SwiftUI view that allows me to navigate to each module based on the initialModule prop, and from React Native I am passing the prop to NavigationContainer's initialRouteName prop.

Now, just like in the attached video, if I navigate on Home module, then on Settings [native screen], then on Contact [RN screen], and then press the back button [which calls popToNative], it will close all the screens. Basically popToNative closes all the React Native screens.

I am not sure if this is the expected behavior or if it's a bug. I would expect popToNative to close only the React Native screens that are on the top of the stack.

Here is the code I am using:

import Brownie
import ReactBrownfield
import SwiftUI
import UIKit

enum HomeDestination: Hashable {
    case rnHome
    case nativeSettings
    case rnContact
}

@available(iOS 16.0, *)
struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        ZStack(alignment: .topTrailing) {
            NavigationStack(path: $path) {
                Color(UIColor.systemBackground)
                    .ignoresSafeArea()
                    .navigationBarHidden(true)
                    .navigationDestination(for: HomeDestination.self) { dest in
                        destinationView(for: dest)
                    }
            }

            FloatingHomeButtons { destination in
                path.append(destination)
            }
            .padding(16)
        }
    }

    @ViewBuilder
    private func destinationView(for dest: HomeDestination) -> some View {
        switch dest {
        case .rnHome:
            RNScreen(initialModule: "Home")
        case .nativeSettings:
            SettingsScreen()
        case .rnContact:
            RNScreen(initialModule: "Contact")
        }
    }
}

private struct FloatingHomeButtons: View {
    let onSelect: (HomeDestination) -> Void

    var body: some View {
        VStack(alignment: .trailing, spacing: 12) {
            Button("Open RN-Home") { onSelect(.rnHome) }
                .buttonStyle(.borderedProminent)

            Button("Open Native-Settings") { onSelect(.nativeSettings) }
                .buttonStyle(.borderedProminent)

            Button("Open RN-Contact") { onSelect(.rnContact) }
                .buttonStyle(.borderedProminent)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
        .allowsHitTesting(true)
    }
}

struct RNScreen: View {
    let initialModule: String

    var body: some View {
        ReactNativeView(
            moduleName: "RNApp",
            initialProperties: [
                "nativeOsVersionLabel":
                    "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)",
                "initialModule": initialModule
            ]
        )
        .ignoresSafeArea()
        .navigationBarHidden(true)
    }
}

@available(iOS 16.0, *)
#Preview {
    ContentView()
}
export default function App({
  nativeOsVersionLabel,
  initialModule = 'Home',
}: AppProps) {
  const initialRouteName: InitialModule =
    initialModule === 'Contact' ? 'Contact' : 'Home';

  return (
    <NativeOsVersionLabelContext.Provider value={nativeOsVersionLabel}>
      <NavigationContainer>
        <Stack.Navigator
          initialRouteName={initialRouteName}
          screenOptions={{ headerShown: false }}
        >
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Contact" component={ContactScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </NativeOsVersionLabelContext.Provider>
  );
}

Reproducible Demo

Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-05-26.at.22.58.58.mov

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions