This episode covers navigation: React Navigation with stack, tab, and drawer, the file-based Expo Router alternative, and deep linking configuration to open specific screens from a URL and universal links.

An app with a single screen is just a demo. Real apps have many screens: lists, details, forms, tabs, and modals. All those transitions are handled by navigation — one of the most important architectural decisions in React Native.
Episode 6 covers the two main approaches: React Navigation, the de-facto library with stack, tab, and drawer, and Expo Router, the file-based approach popular in the Expo ecosystem. You'll also learn deep linking so screens can be opened from a URL.
Start by installing React Navigation and its native dependencies:
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-contextThe @react-navigation/native-stack package provides a modern stack navigator that uses react-native-screens for performance. Don't forget to wrap the app with NavigationContainer.
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { HomeScreen } from "./screens/HomeScreen";
import { DetailScreen } from "./screens/DetailScreen";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}With the setup above, the Home and Detail screens can navigate between each other. To navigate from inside a screen, use navigation.navigate("Detail"), which is provided via props.
When navigating, send parameters via the second argument:
navigation.navigate("Detail", { id: 42 });On the destination screen, read the parameter via route.params.id. TypeScript can type these parameters so navigation is type-safe.
Besides stack, React Navigation provides @react-navigation/bottom-tabs for bottom tabs and @react-navigation/drawer for a sliding menu. A common combination: tabs at the outer level, then a stack inside each tab. This structure is called nested navigation, and the pattern is:
<Tab.Navigator>
<Tab.Screen name="Beranda" component={HomeStack} />
<Tab.Screen name="Profil" component={ProfileScreen} />
</Tab.Navigator>Each component in a tab can be its own Stack.Navigator — that's a nested navigator.
Expo Router replaces navigator code with a folder structure. Every file in the app/ directory becomes a route automatically:
app/
├── _layout.tsx # root layout navigator
├── index.tsx # route "/"
├── detail/
│ └── [id].tsx # route "/detail/:id"
└── settings.tsx # route "/settings"The _layout.tsx file defines the parent navigator — for example stack or tabs — and the other files become screens. Moving between screens is as simple as <Link href="/detail/42"> or router.push("/detail/42").
Expo Router's advantages: routing centralized in the file system, easy to read, automatic deep linking, and uniform team expectations. Disadvantages: it requires an Expo project and gives up a little granular control compared to React Navigation. The choice between them depends on the ecosystem you use.
Deep linking allows an app to be opened directly to a specific screen from outside — for example the URL myapp://detail/42 from a notification or email. In React Navigation, it's configured via the linking property:
const linking = {
prefixes: ["myapp://", "https://myapp.com"],
config: {
screens: {
Home: "home",
Detail: "detail/:id",
},
},
};
<NavigationContainer linking={linking}>
...
</NavigationContainer>With the configuration above, myapp://detail/42 opens the Detail screen directly with the parameter id = 42. Expo Router handles this pattern automatically from the folder structure.
For deep links with the https scheme (universal links on iOS, app links on Android), you need to register your app domain — the apple-app-site-association and assetlinks.json configuration on the server. This is important for a smooth experience when a user opens a link from the browser.
Warning
Deep linking can be misused: other apps could open the myapp:// scheme if it isn't validated. Make sure the handler only processes known URLs and validate the parameters before processing them.
If a new screen is blank, react-native-screens or react-native-safe-area-context probably isn't linked. In a CLI project, run npx react-native run-android after installing to make sure the native module compiles. With Expo, use npx expo install so the versions match.
Screen state is lost when you leave the stack — that's normal behavior. If you want to keep the state (for example form input), store it in a global store like Zustand (episode 7) or move it to the parent navigator.
Episode 6 gave you the roadmap between screens: React Navigation with stack, tab, and drawer, the file-based Expo Router alternative, and deep linking so screens open from external URLs.
Key takeaways:
NavigationContainer wraps all navigators in React Navigation.navigate and read from route.params.app/ folder to routes.linking property.In the next episode, episode 7, we'll discuss networking and data fetching: fetch and Axios with error handling, base URLs per environment, TanStack Query integration for caching and optimistic updates, plus an introduction to state management with Zustand.