Singular stack routes
Edit
Learn how to enforce singular routes.
Singular routes are available from SDK 53 and later. They are currently flagged asdangerouslySingular
due to an upstream issue withreact-native-screens
. Expo is working with Software Mansion to resolve the issue.
While a Stack
normally operates with push
/pop
actions, in advanced scenarios you may wish to enforce a singular version of a screen in the stack. When enabled, any time a new screen is pushed the history will be scanned and any matching screens will be removed to ensure only a singular version exists.
Options
There are two options for conditions:
true
: Use the default singular functionFunction
: Specify your own singular function
The default singular function returns the screen's pathname without search params or the hash.
The singular identifier function has the signature of (routeName: string, params: Record<string, string | string[]>) => string | undefined
.
Assigning singular on the navigator
app
_layout.tsx
index.tsx
[profile].tsx
Using the above example app, you can create a singular constraint to ensure that each [profile]
page is unique.
export default function () { return ( <Stack> <Stack.Screen name="[profile]" dangerouslySingular={({ profile }) => profile} /> </Stack> ); }
If you now navigate to /profile-2
and your history is:
/profile-1
/profile-2
/profile-3
The new history will push the new route, but also remove any previous entries:
/profile-1
/profile-3
/profile-2
Creating singular links
dangerouslySingular
can also be added to the <Link />
component to create links that enforce singular routes.
<Link href="/unique-link" dangerouslySingular /> <Link href="/unique-link" dangerouslySingular={(name) => name === 'unique-link' ? name : undefined} />
Using the imperative API
The imperative API also accepts a dangerouslySingular
option.
router.navigate('/unique-link', { dangerouslySingular: true }); router.navigate('/unique-link', { dangerouslySingular: name => (name === 'unique-link' ? name : undefined), }); router.push('/unique-link', { dangerouslySingular: true }); router.push('/unique-link', { dangerouslySingular: name => (name === 'unique-link' ? name : undefined), });
Navigate vs push
The navigate
and push
events apply the singular constraint differently.
- navigate: Only applies the constraint if the current route is changed
- push: Always applies the constraint
app
_layout.tsx
index.tsx
[profile].tsx
Using the app structure above, if you have the history of:
/profile-1
/profile-2
/profile-2
/profile-3
/profile-2
If you navigate to /profile-2
, the singular constraint will not be applied as the current route is the target route when using navigate
. However, if you push /profile-2
, the singular constraint will be applied.
// Will NOT apply the singular constraint as the current route is the target route router.navigate("/profile-2", { dangerouslySingular: (_, { profile } => profile === 'profile-2' ? profile : undefined ) }) // Will apply the singular constraint router.push("/profile-2", { dangerouslySingular: (_, { profile } => profile === 'profile-2' ? profile : undefined ) })