skip to content

With React Navigation 7, how must linking config be shaped so a product URL opens Product in a tabbed Shop stack with Catalog behind it?

level: middleimportance: should knowfreq 42%

answer

  1. config mirrors the navigator tree
  2. screens inside screens
  3. initialRouteName fills in the back target
  4. paths join the parent's path
  5. exact: true breaks the join

basics

~10 s

React Navigation 7's config.screens must nest exactly like the navigators: tabs, then the Shop stack, then Product. Setting initialRouteName: 'Catalog' on the Shop entry makes a linked Product open with Catalog beneath it.

solid answer

~40 s

The screens map is a tree that has to mirror the navigator tree. If `Product` lives in a `Shop` stack that is a tab inside `HomeTabs`, the config is `HomeTabs: { screens: { Shop: { screens: { Product: 'product/:sku' } } } }`. A flat `Product: 'product/:sku'` at the root produces a state that names `Product` at the root, where no such screen exists, so the link does not land. A matched link to a nested screen builds only the routes on its path, so the Shop stack would hold just `Product` and back would leave the stack; `initialRouteName: 'Catalog'` on the `Shop` entry inserts `Catalog` beneath `Product` when the path matches there. Paths also compose: a child's path is appended to its parent's path unless the child sets `exact: true`.

code

typescript · 25 lines
typescript
import type { LinkingOptions, NavigatorScreenParams } from '@react-navigation/native';

type ShopStackParamList = { Catalog: undefined; Product: { sku: string } };
type HomeTabParamList = { Shop: NavigatorScreenParams<ShopStackParamList>; Cart: undefined };
type RootStackParamList = { HomeTabs: NavigatorScreenParams<HomeTabParamList> };

export const linking: LinkingOptions<RootStackParamList> = {
  prefixes: ['furnistore://', 'https://shop.example.com'],
  config: {
    screens: {
      HomeTabs: {
        screens: {
          Shop: {
            initialRouteName: 'Catalog',
            screens: {
              Catalog: 'catalog',
              Product: { path: 'product/:sku', alias: ['p/:sku'] },
            },
          },
          Cart: 'cart',
        },
      },
    },
  },
};

go deeper

for a junior

Remember that the screens map nests: a screen that holds a navigator gets its own screens object containing that navigator's screens.

for a middle

Explain why a flat entry fails, how initialRouteName supplies the back target for a linked nested screen, and how parent paths compose.

for a senior

Design paths that survive refactors of the navigator tree using exact and alias, and keep parse and stringify symmetric so shared URLs round-trip.

for a principal

Decide how tightly public URLs may couple to the internal navigator tree, and what migration path exists when the tree changes.

## The shape of the app A furniture store app typically has a root stack containing a bottom-tab navigator, and each tab may hold its own stack: - `Root` stack → `HomeTabs` (tabs) and modal screens such as `NotFound` - `HomeTabs` → `Shop` (a stack), `Cart`, `Account` - `Shop` stack → `Catalog`, `Product` A shared product URL, `https://shop.example.com/product/oak-sofa`, should open `Product` in the `Shop` tab with `Catalog` underneath, so back behaves as if the customer had browsed there. ## Rule 1: the config mirrors the navigators In **React Navigation 7**, `config.screens` is a tree: an entry for a screen that renders a navigator can have its own `screens` map. `getStateFromPath` walks that tree to produce **nested state** — a route for `HomeTabs` whose state has a route for `Shop` whose state has a route for `Product`. ```tsx config: { screens: { HomeTabs: { screens: { Shop: { initialRouteName: 'Catalog', screens: { Catalog: 'catalog', Product: 'product/:sku', }, }, Cart: 'cart', }, }, }, } ``` The common mistake is a **flat config** — `Product: 'product/:sku'` directly under the root `screens`. The path still matches, but the state it produces names `Product` as a root route. The root stack has no screen called `Product`, so the state cannot be used and the link does not open the product. ## Rule 2: the back target is not implied A link builds only the routes on its path. Without further configuration, the `Shop` stack's state would contain just `Product`; back from it would leave the stack rather than show the catalog. | Config on the `Shop` entry | `Shop` stack after the link | Back from `Product` | |---|---|---| | none | `Product` | leaves the Shop stack | | `initialRouteName: 'Catalog'` | `Catalog`, `Product` | shows `Catalog` | `initialRouteName` in a path config applies **when the path matches inside that navigator**: React Navigation inserts that route before the matched one. The root `config` object accepts an `initialRouteName` as well, for the root navigator. ## Rule 3: paths compose A nested screen's path is, by default, **relative to its parent's path**. If `Shop` had `path: 'shop'`, then `Product: 'product/:sku'` would match `shop/product/oak-sofa`. Two tools adjust that: - `exact: true` on a child makes its path absolute, ignoring the parents' paths — useful to keep `product/:sku` short while `Shop` has a path of its own. - `alias` lists additional paths that match the same screen, for example an old `p/:sku` URL that marketing already printed. A parent without a `path` — like `HomeTabs` above — contributes nothing, which is why it is common to give paths only to leaf screens. ## Parsing and building Per-screen options sit next to `path`: `parse` converts incoming params (`parse: { qty: Number }`), `stringify` converts outgoing ones when a state is turned back into a path with `getPathFromState`. The same config is used in both directions, so building a share URL for the current product produces the same `product/oak-sofa` a customer can paste back. ## Validation help React Navigation validates the config's shape when the container renders it. An unknown key at the wrong level — say `parse` on the root `config`, or a typo such as `screen` for `screens` — throws an error listing the invalid properties and the ones allowed there. A structurally valid config can still be *semantically* wrong (flat instead of nested), which shows up as links that do nothing. ## What a strong answer shows It draws the navigator tree, writes a config that mirrors it, explains why the flat version fails, adds `initialRouteName` for a sensible back stack, and mentions how parent paths compose with `exact` and `alias` as escape hatches.

  • Why does TypeScript accept a nested screens map under HomeTabs but not under Cart?
    The `PathConfigMap` type only allows `screens` and `initialRouteName` for a route whose param type is `NavigatorScreenParams<…>`, meaning it renders a nested navigator. `Cart` is a plain screen, so its entry may only be a path string or a path object with options such as `parse`.
  • Marketing printed short URLs like /p/oak-sofa; how do you support them without a second screen?
    Add `alias: ['p/:sku']` to the `Product` path config. An alias is an extra pattern that matches the same screen, so both `product/oak-sofa` and `p/oak-sofa` open `Product` with `sku` set, while building a path from state still produces the main `product/:sku` form.

saying these in an interview costs you the question

  • A flat Product: 'product/:sku' at the root works for nested screens too.
  • A linked Product automatically gets Catalog beneath it because Catalog is the stack's first screen.
  • A child screen's path always ignores its parent's path.
  • React Navigation infers the nesting from the navigators, so the config shape does not matter.