Alt description missing in image

useRouteParams()

A tiny abstraction layer that retrieves the parameters of a route in both the Expo Router and Next.js app routers. Server-side, in the browser and on iOS or Android:

import { useRouteParams } from '@green-stack/navigation'
const routeParams = useRouteParams(routeProps)

TypeScript should complain if you don’t, but make sure to include the route’s screen props when using this hook, as it relies on them to access the route parameters in Next.js.

Options

useRouteParams accepts an optional second argument:

const routeParams = useRouteParams(routeProps, { reactive: true })
OptionTypeDefaultDescription
reactivebooleantrueWhen true, the hook subscribes to shallow URL changes made via router.setParams(..., { shallow: true }) and re-renders the component when search params change.

Reactive mode (default)

By default, useRouteParams subscribes to shallow search param changes on web. This means if another component calls router.setParams({ filter: 'new' }, { shallow: true }), any component using useRouteParams with reactive: true will re-render with the updated params:

// This component re-renders when shallow params change
const MyFilter = (props) => {
    const params = useRouteParams(props) // reactive: true by default
    return <Text>{params.filter}</Text>
}

On Expo, useLocalSearchParams() is already reactive by nature, so the reactive option has no effect on mobile.

Non-reactive mode

Pass { reactive: false } when you don’t need the component to respond to shallow URL changes. This is useful for components high in the tree (like route wrappers) where re-rendering would be expensive:

// This component won't re-render on shallow param changes
const routeParams = useRouteParams(props, { reactive: false })
⚠️

The built-in UniversalRouteScreen wrapper uses { reactive: false } by default to avoid re-rendering the entire page tree on shallow param updates. If your screen component needs to respond to shallow changes, use useRouteParams directly within the screen component itself (where reactive defaults to true).

Full Type Reference

View UseRouteParamsOptions type
type UseRouteParamsOptions = {
    /** When `true`, the hook subscribes to shallow URL changes
     ** (via `router.setParams(..., { shallow: true })`)
     ** and re-renders the component when search params change.
     ** On Expo, `useLocalSearchParams()` is already reactive, so this option has no effect.
     ** @default true */
    reactive?: boolean
}

React Portability Patterns

Each environment has its own ways of accessing route parameters. This is why there are also versions specifically for each of those environments:

        • useRouteParams.expo.ts
        • useRouteParams.next.ts
        • useRouteParams.ts
        • useRouteParams.types.ts

Where useRouteParams.next.ts covers the Next.js app router, and useRouteParams.expo.ts covers Expo Router. The main useRouteParams.ts retrieves whichever implementation was provided to the <UniversalAppProviders> component, which is further passed to <CoreContext.Provider/>:

ExpoRootLayout.tsx
import { useRouteParams as useExpoRouteParams } from '@green-stack/navigation/useRouteParams.expo'
 
// ... Later ...
 
<UniversalAppProviders
    useContextRouteParams={useExpoRouteParams}
>
    ...
</UniversalAppProviders>
NextRootLayout.tsx
import { useRouteParams as useNextRouteParams } from '@green-stack/navigation/useRouteParams.next'
 
// ... Later ...
 
<UniversalAppProviders
    useContextRouteParams={useNextRouteParams}
>
    ...
</UniversalAppProviders>

While the useRouteParams.types.ts file ensures both implementations are compatible with the same interface, allowing you to use the same useRouteParams() hook across both Expo and Next.js environments.

Why this pattern?

The ‘React Portability Patterns’ used here are designed to ensure that you can easily reuse optimized versions of hooks across different flavours of writing React.

On the one hand, that means it’s already set up to work with both Expo and Next.js in an optimal way.

But, you can actually add your own implementations for other environments, without having to refactor the code that uses the useRouteParams hook.

Supporting more environments

Just add your own useRouteParams.<environment>.ts file that respects the shared types, and then pass it to the <UniversalAppProviders> component as useContextRouteParams.