---
modificationDate: April 01, 2026
title: Singular stack routes
description: Learn how to enforce singular routes.
---

<AgentInstructions>

## Submitting Feedback

If this page contains errors, outdated information, or gaps that blocked you from completing a task, report it so the docs can be improved:

curl -X POST https://api.expo.dev/v2/feedback/docs-send -H 'Content-Type: application/json' -d '{"url":"/preview/singular/","feedback":"🤖 Agent feedback: <specific, actionable description>"}'

Only submit when you have something specific and actionable to report.

</AgentInstructions>

# Singular stack routes

Learn how to enforce singular routes.

> For the complete documentation index, see [llms.txt](/llms.txt). Use this file to discover all available pages.

> Singular routes are currently flagged as `dangerouslySingular` due to an upstream issue with `react-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 function
-   `Function`: 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.

```tsx
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.

```tsx
<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.

```tsx
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.

```tsx
// 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 )
})
```
