skip to content

In React Navigation 7, what happens when a URL matches no linking path, and how do you route it to a NotFound screen?

level: middleimportance: should knowfreq 34%

answer

  1. no match means no state
  2. launch falls back to the initial screen
  3. '*' is the catch-all pattern
  4. wildcard loses to specific paths
  5. filter skips URLs on purpose

basics

~20 s

An unmatched URL yields no state in React Navigation 7, so a cold start opens the default initial screen and a running app ignores the link. A screen with the path '*' catches every path that nothing more specific matches.

solid answer

~40 s

`getStateFromPath` returns `undefined` when no pattern matches. At launch the container then has no linked state and opens on its normal initial screen; for a URL arriving while the app runs, the listener has no state and does nothing. Either way the customer tapped a link and nothing explains why they are on the home screen. Adding a screen such as `NotFound: '*'` fixes that: `*` matches any path, and React Navigation ranks wildcard segments below static and param segments, so `product/:sku` still wins and only truly unknown paths fall through. Two related tools: `filter` lets you skip URLs on purpose, such as an auth redirect, and `alias` or a wrapping `getStateFromPath` can map old URLs onto current screens instead of sending them to `NotFound`.

code

typescript · 27 lines
typescript
import { getStateFromPath, type LinkingOptions } from '@react-navigation/native';
import type { RootStackParamList } from './types';

export const linking: LinkingOptions<RootStackParamList> = {
  prefixes: ['furnistore://', 'https://shop.example.com'],
  filter: (url) => !url.includes('/oauth/callback'),
  config: {
    screens: {
      HomeTabs: {
        screens: {
          Shop: {
            initialRouteName: 'Catalog',
            screens: {
              Catalog: 'catalog',
              Product: 'product/:sku',
            },
          },
        },
      },
      NotFound: '*',
    },
  },
  getStateFromPath(path, options) {
    const rewritten = path.replace(/^\/?item\//, 'product/');
    return getStateFromPath(rewritten, options);
  },
};

go deeper

for a junior

Know that an unknown path does not navigate anywhere by itself, and that a screen with the path '*' catches unmatched links.

for a middle

Explain cold versus warm behaviour for unmatched URLs, the ranking that keeps specific paths ahead of the wildcard, and what filter does.

for a senior

Plan for legacy URLs with alias or a wrapped getStateFromPath, validate matched params on the target screen, and tell config bugs apart from undelivered links.

for a principal

Own the long-lived URL contract: which shared links must keep working across releases and how removed content degrades for customers.

## What "no match" means In **React Navigation 7**, an incoming URL goes through `getStateFromPath` after its prefix is stripped. When no pattern in `config.screens` matches, that function returns **`undefined`**, and the behaviour depends on when the URL arrived: 1. **At launch (cold start).** The container asked `getInitialURL` for the launch URL, got a URL, but could not turn it into state. It renders with no linked state, so the root navigator starts on its usual initial screen. 2. **While the app runs (warm).** The URL listener computes no state and simply returns. No navigation happens. It also returns nothing when the URL has **no matching prefix** — a link on a domain or scheme you forgot to list in `prefixes`. And a `getStateFromPath` that throws is caught and logged with `console.error`, again producing no state. In a furniture store that means a customer taps a shared link for a discontinued product category, the app opens on the home tab, and nothing tells them why. That silent fallback is the thing to fix. ## Catching unknown paths with a wildcard A path pattern of **`*`** matches any path. The usual setup is a `NotFound` screen at the root: ```tsx config: { screens: { HomeTabs: { screens: { /* ... */ } }, NotFound: '*', }, } ``` React Navigation sorts candidate patterns so that **static segments beat param segments, and both beat wildcards**. `product/:sku` therefore still matches `product/oak-sofa`; only a path that nothing else matches ends up at `NotFound`. The wildcard also catches unmatched paths that would otherwise have been nested — the whole path is matched against the configs, not segment by segment in isolation. | Incoming path | Matches | Result | |---|---|---| | `product/oak-sofa` | `product/:sku` | `Product` with `sku: 'oak-sofa'` | | `catalog` | `catalog` | `Catalog` | | `sale/2019-spring` | only `*` | `NotFound` | | (no matching prefix) | — | not handled at all | The `NotFound` screen can read its own route path to show what was requested and offer a way back to the catalog. ## Unknown is not always "not found" Some URLs are known but should not become screens, and some are old but still meaningful: - **`filter`** — a function on the linking options that receives the URL and returns whether React Navigation should handle it. Returning `false` for an authentication redirect URL, for example, keeps it from being matched (and from landing on `NotFound`). - **`alias`** — extra patterns on a screen's path config, such as `p/:sku` for old short links, so they open the real screen. - **A custom `getStateFromPath`** — the linking options accept your own function. Wrapping the default one, which `@react-navigation/native` exports, lets you rewrite a legacy path before matching, or return a specific state for it. ## Where "matched" still is not "valid" A wildcard proves the path had some shape, not that it is safe. `Product` matched with `sku: '../../admin'` is still a matched link. The target screen must handle an unknown `sku` — show "this item is no longer available" rather than crash or render an empty page — and must treat the value as untrusted input. ## Debugging an unmatched link - Check the prefix: is the exact scheme and host, including `https` and any subdomain, in `prefixes`? - Call the exported `getStateFromPath` with the path and your config in a unit test and inspect the result. - Remember that a link the OS never delivered looks identical from the app's point of view: nothing happens. If `getInitialURL` or the listener never fires, the problem is native registration, not the config. ## What a strong answer shows It states the silent behaviour for both cold and warm URLs, adds `'*'` with the ranking rule that keeps specific paths winning, and distinguishes deliberately skipped URLs (`filter`) and legacy URLs (`alias`, custom `getStateFromPath`) from truly unknown ones.

  • Why does NotFound: '*' not swallow /product/oak-sofa?
    React Navigation ranks candidate patterns: static segments outrank param segments, which outrank wildcards. `product/:sku` is more specific than `*`, so it wins for any product path. The wildcard only matches what no other pattern accepts.
  • When should a URL be filtered out rather than sent to NotFound?
    When it is a known URL that is not meant to become a screen — an authentication redirect, a link another part of the app consumes. `filter` returning `false` makes React Navigation ignore it entirely, so it neither navigates nor lands on NotFound.

Like a mail room's sorting frame: letters go to the most specific pigeonhole that fits, and only what fits nowhere lands in the dead-letter tray instead of vanishing.

saying these in an interview costs you the question

  • An unmatched URL throws and crashes the app on launch.
  • A '*' route catches everything, so it must be listed last to avoid shadowing product paths.
  • React Navigation shows a built-in not-found screen for unknown paths.
  • If a link does nothing, the linking config must be wrong.