Singular stack routes
Edit page
Learn how to enforce singular routes.
Singular routes are available from SDK 53 and later. They are currently flagged asdangerouslySingulardue 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.tsxindex.tsx[profile].tsxUsing 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.tsxindex.tsx[profile].tsxUsing 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 ) })