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:
Both hooks are typed and accessed the same way. However, the only difference is how frequently they update. Consider the following example:
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:
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:
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:
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:
Local: evanbacon Global: evanbacon
Pressing "Visit charlie" pushes a new instance of /[user]
with user=charlie, and logs the following:
# This log came from the new screen
Local: charlie Global: charlie
# This log came from the first screen
Local: evanbacon Global: charlie
Pressing "Visit james" has a similar effect:
# This log came from the new, "/james" screen
Local: james Global: james
# This log came from the "/evanbacon" screen
Local: evanbacon Global: james
# This log came from the "/charlie" screen
Local: charlie Global: james
Results:
useGlobalSearchParams
made the background screens re-render when the search params changed. It can cause performance issues if overused.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.Both the useLocalSearchParams
and useGlobalSearchParams
can be statically typed using a generic:
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:
import { Text } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
export default function Route() {
const { user } = useLocalSearchParams<{
everything: string[];
}>();
return <Text>User: {user}</Text>;
}
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.
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,
}}
/>
);
}