skip to content

With React Navigation 7, how do you pass an order id to a delivery-tracking screen and read it inside that screen?

level: juniorimportance: must knowfreq 76%

answer

  1. second argument of navigate
  2. route prop on the target screen
  3. useRoute for nested children
  4. initialParams merged underneath
  5. send the id, not the order

basics

~10 s

Call navigation.navigate('Tracking', { orderId }), where the second argument is the params object, then read route.params.orderId in the Tracking screen, or call useRoute() from any component rendered inside that screen.

solid answer

~50 s

In React Navigation 7 params are the second argument of a navigation call: `navigation.navigate('Tracking', { orderId: 'A123' })`. The target screen receives a `route` prop and reads `route.params.orderId`. A component nested deeper inside that screen, such as a map or a status timeline, can call `useRoute()` to get the same route object from context instead of having it passed down, and `useNavigation()` gives it the navigation object in the same way. Defaults come from `initialParams` on the screen definition, and whatever the caller passes is merged on top of them. Params become part of the navigation state, so pass the smallest serializable value, the id, and let the tracking screen load the order itself. With a TypeScript param list such as `{ Tracking: { orderId: string } }`, a missing or misspelled `orderId` becomes a compile error.

code

tsx · 29 lines
tsx
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { Button, Text, View } from 'react-native';

type RootStackParamList = {
  Orders: undefined;
  Tracking: { orderId: string };
};

export function OrdersScreen({
  navigation,
}: NativeStackScreenProps<RootStackParamList, 'Orders'>) {
  return (
    <Button
      title="Track order A123"
      onPress={() => navigation.navigate('Tracking', { orderId: 'A123' })}
    />
  );
}

export function TrackingScreen({
  route,
}: NativeStackScreenProps<RootStackParamList, 'Tracking'>) {
  const { orderId } = route.params;
  return (
    <View>
      <Text>Tracking order {orderId}</Text>
    </View>
  );
}

go deeper

for a junior

Recall the pair: params go in the second argument of navigate and come out of route.params in the target screen, or through useRoute in a child component.

for a middle

Explain that params live on the route in navigation state, how initialParams merge underneath caller values, and why the screen should re-read params on change rather than copy them once.

for a senior

Show that you keep params minimal, an id rather than an entity, type them through a param list, and treat a screen that can be reached without its id as a bug to prevent at compile time.

for a principal

Frame params as the public contract of each screen: small, typed and serializable, so screens stay independently reachable from links, restored state and other teams' features.

## What a route param is In **React Navigation 7**, every screen you visit is represented by a **route**: a small object in the navigator's state with a `key`, a `name` (the screen name you registered, such as `Tracking`) and optional **`params`**. Params are how one screen hands input to another. They are not React props passed by a parent component; they are data stored on the route inside the **navigation state**, and React Navigation delivers them to the screen when it renders it. In a delivery app the classic case is an orders list whose rows open a tracking screen for one order. The list knows which order was tapped; the tracking screen needs that order's id to subscribe to its courier position and status. ## Passing params when navigating Every navigation method that targets a screen takes the params object as its **second argument**: - `navigation.navigate('Tracking', { orderId: 'A123' })` is the everyday call. - The stack-only methods `push`, `replace` and `popTo` accept params in the same position. - Since `@react-navigation/core` 7.20.0 the older object form `navigate({ name, params })` is **deprecated**; write `navigate(name, params, options)` instead. - A screen with no required params can be opened with just its name, `navigation.navigate('Orders')`. The values should be plain data: strings, numbers, booleans, arrays and plain objects. Passing the whole order object is a common junior habit, but the params are a copy taken at the moment of navigation, so a status that changes a minute later is not reflected in them. Passing the id and reading the live order from the app's data layer avoids that. ## Reading params in the target screen There are two ways to read the params, and they return the same object: | Where the code runs | How it reads the params | |---|---| | The screen component registered with the navigator | the `route` prop: `route.params.orderId` | | A component rendered anywhere inside that screen | `const route = useRoute()`, then `route.params.orderId` | | Code that also needs to navigate | the `navigation` prop, or `useNavigation()` in a child | Key points about the hooks: 1. `useRoute()` returns the route of the **screen the component is rendered inside**, not the route of whatever screen happens to be focused. A `TrackingMap` component inside `Tracking` always sees the tracking route. 2. Outside any screen, `useRoute()` throws with a message asking whether the component is inside a screen in a navigator. 3. When params change, for example because the screen calls `navigation.setParams`, the screen re-renders with the new `route.params`, so values derived from them should be read during render or in an effect keyed on the param rather than copied into state once. ```tsx import { useRoute, type RouteProp } from '@react-navigation/native'; import { Text } from 'react-native'; type RootStackParamList = { Orders: undefined; Tracking: { orderId: string } }; export function TrackingTitle() { const route = useRoute<RouteProp<RootStackParamList, 'Tracking'>>(); return <Text>Order {route.params.orderId}</Text>; } ``` ## Defaults and missing params A screen definition can carry **`initialParams`**. React Navigation merges them underneath the params a caller passes, so a caller-supplied value wins and a missing key falls back to the default. This is useful for optional values such as a default tab on the tracking screen, but it is a poor fit for a required id: there is no sensible default order. If a screen is opened without params and has no `initialParams`, `route.params` is `undefined`, and reading `route.params.orderId` throws a `TypeError`. Two defences are common: - declare the param as required in a TypeScript param list, so `navigate('Tracking')` without an id fails to compile; - read optional params defensively with `route.params?.tab`. ## Typing the params In a TypeScript project each navigator gets a **param list**, a type alias that maps screen names to their params: `Orders: undefined` for a screen with none, `Tracking: { orderId: string }` for one that needs an id. The screen then declares its props with the navigator's helper type, for example `NativeStackScreenProps<RootStackParamList, 'Tracking'>`, and both `navigate` and `route.params` are checked against the list. In a large app that type becomes the contract between screens, which is what makes renaming a param safe. ## Common mistakes - Reading `props.orderId`: params are never spread onto the screen's props; they live on `route.params`. - Treating params as global state: each route has its own params, and two tracking screens for two orders hold two different values. - Storing a param in `useState` once on mount and missing a later update to it.

  • What happens if the tracking screen reads route.params.orderId but was opened with navigate('Tracking') and no params?
    Without `initialParams`, `route.params` is `undefined`, so `route.params.orderId` throws a `TypeError` at render. A TypeScript param list that declares `Tracking: { orderId: string }` prevents it by rejecting the call without params at compile time; optional params should be read as `route.params?.tab`.
  • Why call useRoute() in a child component instead of passing route down as a prop?
    `useRoute()` reads, from context, the route of the screen the component is rendered inside, so a map or status component deep in the tracking screen gets `orderId` without every intermediate component forwarding it. It throws if the component is rendered outside any screen, which makes misuse obvious.

saying these in an interview costs you the question

  • Params are spread onto the screen's props, so the screen reads props.orderId.
  • Pass the whole order object so the tracking screen never has to load it.
  • useRoute returns the route of whichever screen is currently focused.
  • Params are shared state that every screen in the stack can read.
  • Copying route.params into useState once is enough to follow later updates.