skip to content

In Expo Router, what do usePathname and useSegments return on /product/42, and when would you reach for each?

level: middleimportance: should knowfreq 32%

answer

  1. one is the URL, one is the file
  2. pathname: normalized, no query
  3. segments: bracket names kept
  4. groups appear only in segments
  5. segments suit structural checks

basics

~20 s

usePathname returns the normalized URL path without the query, '/product/42', while useSegments returns the matched file segments as written, ['product', '[id]'], including any (group) names, so pathname suits display and analytics and segments suit structural checks.

solid answer

~40 s

`usePathname()` from `expo-router` returns the **URL path** of the focused route with parameters filled in and no query string: on `/product/42?ref=home` it is `'/product/42'`, and route groups never appear in it. `useSegments()` returns the **file-system segments** of the matched route, not normalized: `['product', '[id]']`, and if the file lives in a group, the group too, as in `['(shop)', 'product', '[id]']`. Use the pathname for things about the concrete location: analytics screen names, share links, highlighting an active link. Use segments for **structural** decisions that must not depend on parameter values: "am I inside the `(shop)` group?", "is this any product page?", or building a link that stays inside the current tab. Both follow the globally focused route, so they re-render when it changes.

code

tsx · 11 lines
tsx
// src/components/shop-banner.tsx (rendered from a layout)
import { usePathname, useSegments } from 'expo-router';
import { Text } from 'react-native';

export function ShopBanner() {
  const pathname = usePathname(); // '/product/42'
  const segments = useSegments(); // ['(shop)', 'product', '[id]']

  if (segments[0] !== '(shop)') return null; // group check: segments only
  return <Text accessibilityRole="header">You are viewing {pathname}</Text>;
}

go deeper

for a junior

Recall that usePathname returns the URL path with values filled in and useSegments returns the file path segments, brackets and groups included.

for a middle

Explain normalization, why groups appear only in segments, and pick the right hook for analytics versus a group or route-shape check.

for a senior

Keep these global hooks out of hot components, read screen params from useLocalSearchParams, and use typed segments to make structural checks refactor-safe.

for a principal

Standardise how the app derives location for analytics and structural logic so route refactors do not silently break tracking or conditional UI.

## Two views of the same location Expo Router exposes the focused route in two shapes, both from `expo-router`: - **`usePathname()`** returns the current location **as a URL path**, without search parameters. Dynamic segments are **normalized**, meaning replaced by their values. - **`useSegments()`** returns the matched route **as file-system segments**, **not normalized**: bracketed names stay as written and group folders are included. For the shop app with `src/app/(shop)/product/[id].tsx` and the URL `/product/42?ref=home`: | Hook | Returns | Contains groups? | Contains query? | |---|---|---|---| | `usePathname()` | `'/product/42'` | no | no | | `useSegments()` | `['(shop)', 'product', '[id]']` | yes | no | For a catch-all docs page, `/docs/guides/install` served by `src/app/docs/[...slug].tsx`, the pathname is `'/docs/guides/install'` while the segments keep the file's own catch-all name instead of the three values. ## When the pathname is the right tool The pathname answers "**where exactly is the user?**": - screen-view analytics and logging, where `/product/42` versus `/product/43` matters; - a share button that builds a link to the current page; - marking the active item in a web-style navigation bar; - comparing against a known URL, such as `pathname === '/settings'`. Because it is normalized, the pathname is the same string a user would see in a browser's address bar. ## When segments are the right tool Segments answer "**which part of the route tree is this?**", independent of parameter values: - **Group checks** such as `segments[0] === '(shop)'`, which the pathname cannot express because groups are invisible in URLs. - **Route-shape checks** such as "is this any product page?" via `segments.includes('[id]')`, instead of a fragile regex over pathnames. - **Tab-preserving links**: a shared component can read the first segment and build an href that stays in the current group, a pattern Expo's typed-routes guide uses for a profile link reachable from two tab groups. With typed routes, `useSegments` can be narrowed with a generic listing the possible segment tuples, which turns those checks into compile-time-checked comparisons. ## Update behaviour Both hooks read the **globally focused** route. A component using them re-renders whenever navigation changes the focused route, including in background stack screens. That is fine in a layout or a small header component, but reading them in many list rows multiplies the cost. For a screen's **own** parameters, `useLocalSearchParams()` is the better source. ## Worked example across the shop app Take three URLs in the shop app, with product screens inside a `(shop)` group and a docs catch-all outside it: | URL | `usePathname()` | `useSegments()` | |---|---|---| | `/` served by `(shop)/index.tsx` | `'/'` | starts with `'(shop)'` | | `/product/42?ref=home` | `'/product/42'` | `['(shop)', 'product', '[id]']` | | `/docs/guides/install` | `'/docs/guides/install'` | `'docs'` then the catch-all's file name | Three decisions follow from the table: 1. A **"back to shop" button** should appear only inside the group: test `segments[0] === '(shop)'`. 2. **Analytics** should record `'/product/42'`, so product pages can be counted individually. 3. A rule such as "hide the cart icon on every product page" should test the segment `'[id]'` under `'product'`, so it keeps working whatever the id is. ## Common mistakes 1. **Parsing the pathname to find a parameter.** `pathname.split('/')[2]` breaks when the URL shape changes; read the parameter from `useLocalSearchParams()` instead. 2. **Looking for a group in the pathname.** `'/(shop)/product/42'` never occurs; groups exist only in segments. 3. **Comparing segments to values.** `segments[1] === '42'` never matches, because segments hold `'[id]'`, not the value. 4. **Expecting the query string.** Neither hook includes `?ref=home`; search parameters come from the params hooks. ## What interviewers look for A clear statement that one hook is the **URL** and the other is the **file path**, a concrete example of each, and at least one case where only segments work, usually a group check. Candidates who know that dynamic segments are filled in one and left bracketed in the other rarely misuse either.

  • Why not read the product id with usePathname().split('/')?
    It couples the screen to the URL's exact shape and breaks when a segment is added or a group layout changes. `useLocalSearchParams()` returns `id` by name, already decoded, and is scoped to the screen that owns it.
  • What would useSegments() return for /docs/guides/install served by src/app/docs/[...slug].tsx?
    The file segments, `docs` followed by the catch-all's bracketed file name, not the three URL values. The values live in `useLocalSearchParams()` as `slug: ['guides', 'install']`, while the pathname is `'/docs/guides/install'`.

saying these in an interview costs you the question

  • usePathname includes the route group, such as /(shop)/product/42.
  • useSegments returns the parameter values, such as ['product', '42'].
  • usePathname returns the query string along with the path.
  • Parsing the pathname is the recommended way to read an id.
  • Both hooks return exactly the same data in different formats.