HomeGuidesReferenceLearn
ArchiveExpo SnackDiscord and ForumsNewsletter

Use search parameters

Learn how to access and modify search parameters in your app.


Search parameters (also known as query params) are serializable fields that can be added to a URL. They are commonly used to pass data between pages. Expo Router provides hooks for accessing and modifying these parameters.

In nested apps, you'll often have multiple pages mounted at the same time. For example, a stack has the previous page and current page in memory when a new route is pushed. Because of this, Expo Router provides two different hooks for accessing search parameters:

  • useLocalSearchParams: Returns the search parameters for the current component. It only updates when the global URL conforms to the route.
  • useGlobalSearchParams: Returns the global URL regardless of the component. It updates on every search param change and might cause components to update extraneously in the background.

Both hooks are typed and accessed the same way. However, the only difference is how frequently they update. Consider the following example:

Local versus global search parameters

The example below demonstrates the difference between useLocalSearchParams and useGlobalSearchParams. It uses the following app directory structure:

app
_layout.js
index.js
[user].js

1

The Root Layout is a stack navigator:

app/_layout.js
import { Stack } from 'expo-router';

export default function Layout() {
  return <Stack />;
}

2

The initial route redirects to the dynamic route app/[user].js, with user=evanbacon:

app/index.js
import { Redirect } from 'expo-router';

export default function Route() {
  return <Redirect href="/evanbacon" />;
}

3

The dynamic route app/[user] prints out the global and local search parameters. It also allows for pushing new instances of the same route with different search parameters:

app/[user].js
import { Text, View } from 'react-native';
import { useLocalSearchParams, useGlobalSearchParams, Link } from 'expo-router';

const friends = ['charlie', 'james']

export default function Route() {
  const glob = useGlobalSearchParams();
  const local = useLocalSearchParams();

  console.log("Local:", local.user, "Global:", glob.user);

  return (
    <View>
      <Text>User: {local.user}</Text>
      {friends.map(friend => (
        <Link key={friend} href={`/${friend}`}>
          Visit {friend}
        </Link>
      ))}
    </View>
  );
}

4

When the app starts, the following log is printed:

Terminal
Local: evanbacon Global: evanbacon

Pressing "Visit charlie" pushes a new instance of /[user] with user=charlie, and logs the following:

Terminal
# This log came from the new screenLocal: charlie Global: charlie# This log came from the first screenLocal: evanbacon Global: charlie

Pressing "Visit james" has a similar effect:

Terminal
# This log came from the new, "/james" screenLocal: james Global: james# This log came from the "/evanbacon" screenLocal: evanbacon Global: james# This log came from the "/charlie" screenLocal: charlie Global: james

Results:

  • useGlobalSearchParams made the background screens re-render when the search params changed. It can cause performance issues if overused.
  • Global re-renders are executed in order of the stack, so the first screen is re-rendered first, then the user=charlie screen is re-rendered after.
  • useLocalSearchParams remained the same, even when the global search params changed. You can leverage this behavior for data fetching to ensure the previous screen's data is still available when you navigate back.

Statically-typed search parameters

Both the useLocalSearchParams and useGlobalSearchParams can be statically typed using a generic:

app/search.tsx
import { Text } from 'react-native';
import { useLocalSearchParams } from 'expo-router';

export default function Route() {
  const { user } = useLocalSearchParams<{ user: string }>();

  return <Text>User: {user}</Text>;
}

Any additional search parameters are to be typed optionally:

const { user, query } = useLocalSearchParams<{ user: string; query?: string }>();

When used with the rest syntax (...), implements a string array:

app/[...everything].tsx
import { Text } from 'react-native';
import { useLocalSearchParams } from 'expo-router';

export default function Route() {
  const { user } = useLocalSearchParams<{
    everything: string[];
  }>();

  return <Text>User: {user}</Text>;
}

Updating search parameters

Search parameters can be updated using the router.setParams function from the imperative API. The following example uses a <TextInput> to update the search parameter q. This action will not push anything new to the history stack.

app/search.tsx
import { useLocalSearchParams, router } from 'expo-router';
import React from 'react';
import { TextInput, View } from 'react-native';

export default function Page() {
  const params = useLocalSearchParams<{ q?: string }>();
  const [search, setSearch] = React.useState(params.q);

  return (
    <TextInput
      value={search}
      onChangeText={search => {
        setSearch(search);
        router.setParams({ q: search });
      }}
      placeholderTextColor="#A0A0A0"
      placeholder="Search"
      style={{
        borderRadius: 12,
        backgroundColor: '#fff',
        fontSize: 24,
        color: '#000',
        margin: 12,
        padding: 16,
      }}
    />
  );
}

Route parameters versus search parameters

Route parameters are used to match a route, while search parameters are used to pass data between routes. Consider the following structure, where a route parameter is used to match the user route:

app
index.tsx
[user].tsxuser is a route parameter

When the app/[user] route is matched, the user parameter is passed to the component and never a nullish value. Both search and route parameters can be used together and are accessible with the useLocalSearchParams and useGlobalSearchParams hooks:

app/[user].tsx
import { useLocalSearchParams } from 'expo-router';

export default function User() {
  const {
    // The route parameter
    user,
    // An optional search parameter.
    tab,
  } = useLocalSearchParams<{ user: string; tab?: string }>();

  console.log({ user, tab });

  // Given the URL: `/bacon?tab=projects`, the following is printed:
  // { user: 'bacon', tab: 'projects' }

  // Given the URL: `/expo`, the following is printed:
  // { user: 'expo', tab: undefined }
}

When a route parameter is changed, the component will re-mount.

app/[user].tsx
import { Text } from 'react-native';
import { router, useLocalSearchParams, Link } from 'expo-router';

export default function User() {
  // All three of these will change the route parameter `user`, and add a new user page.
  return (
    <>
      <Text onPress={() => router.setParams({ user: 'evan' })}>Go to Evan</Text>
      <Text onPress={() => router.push('/mark')}>Go to Mark</Text>
      <Link href="/charlie">Go to Charlie</Link>
    </>
  );
}