Skip to main content

Stack

What a stack is and where stacks live, how to build one, every command of a stack, the way back, and a whole stack at once for a deep link.

SectionStackDraftDocumented, not reviewed yet: the API can change.

The stack is the main container of an app: a list opens an item, the item opens its comments, and back goes the same way down. Stack builds one, and push and pop move in it:

Navigation.ts
import { createNavigation, Stack } from '@apexnative/navigation';
import { HomeScreen } from './screens/Home';
import { DetailsScreen } from './screens/Details';

export const Navigation = createNavigation({
screens: { Home: HomeScreen, Details: DetailsScreen },
root: Stack('Home'),
});

Home shows first, with a top bar. navigation.push('Details') from Home shows Details over it, and back returns to Home.

This page answers:

  1. What a stack is, and where stacks live in an app.
  2. How to build one, starting with one screen or several.
  3. The commands of a stack: push, back, replace.
  4. The way back: what the user does, and how the app knows.
  5. A whole stack at once, for a deep link.

What a stack is​

A stack is screens one over another: the top one shows, and back goes to the one under it. The screens under the top one stay mounted, with their state and scroll, so back returns to them as they were.

iOS
Android

The stack is the platform's: a UINavigationController on iOS, the library's own stack of views on Android. It owns one top bar for all its screens. Each screen gives the bar its title and buttons through its options, so the bar changes with the screen as it comes in. See Top bar.

Where stacks live​

Wherever the user moves on from a screen, there is a stack. A screen named alone in these places gets a stack around it, so it can push:

PlaceThe stack
The rootroot: 'Home' is the same as root: Stack('Home').
Each tabEach tab has its own stack, and keeps it while the user looks at other tabs. See Tabs.
A modalA modal has its own stack, unless stack: false. See Modals.
The center of a side menu, a column of a split viewThe same as the root. See Side menu, Split view.

Where nothing moves on, a screen stays on its own, without a stack and its top bar: the menus of a side menu, the inspector of a split view, an overlay.

Building a stack​

Stack takes its screens from the bottom one to the top one, each a screen of screens by its name. Most stacks start with one, as above.

A stack can also start deeper, with the screens under the top one already in place. A screen with props goes as Screen(name, props):

import { Screen, Stack } from '@apexnative/navigation';

Stack('Inbox', Screen('Chat', { userId: '7' }));

Chat shows, and back goes to Inbox. A stack can start as deep as it needs, each screen with props of its own:

Stack(
Screen('Inbox', { folder: 'work' }),
Screen('Chat', { userId: '7' }),
Screen('Profile', { userId: '7' }),
Screen('Photo', { photoId: '42' }),
);

Photo shows; back goes to Profile, Chat, then Inbox. Each screen gets only its own props, and TypeScript checks each against its screen.

Screen(…).options() gives a screen options for this show only, over those of its definition:

Stack(
'Inbox',
Screen('Chat', { userId }).options((s) => s.hideBottomTabs()),
);

The stack itself takes no options: they go on its screens, so a screen looks the same in every stack it is pushed into. A screen's own options go in defineScreen, those of many screens in a group or in presentation.push. See Where a screen gets them.

The commands of a stack​

A screen's commands act on the stack the screen is in. Each one changes the screens of the stack, and the top one is what the user sees:

iOS
Android

In a screen, useNavigation gives the commands:

src/screens/Chat.tsx
import { Button, View } from 'react-native';
import { useNavigation } from '@apexnative/navigation';

function Chat({ userId }: { userId: string }) {
const navigation = useNavigation();

const openProfile = () =>
navigation.push('Profile', { props: { userId } });

return (
<View>
<Button title="Profile" onPress={openProfile} />
<Button title="Back" onPress={() => navigation.pop()} />
</View>
);
}

Profile comes in over Chat; Back on either screen goes one screen down.

  • push resolves with the id of the new screen. popTo takes such an id: keep it to come back to that screen later.
  • popToRoot goes back to the first screen of the stack, past all the others at once.
  • replace leaves the new screen alone in the stack, so there is no back. It comes in as a push does. After a payment, replace('OrderPlaced'), so back can't lead to a second payment.
  • setStack gives the stack several new screens at once: the last one shows, and back goes down the others. The screens that were there unmount, even one with the same name.

Code outside a screen, such as a store or a timer, uses the navigation object of the package: its commands act on the stack the user is looking at. See Where the commands come from.

goBack() and the checks canPop() and canGoBack() work on stacks too: see Going back. The way a screen comes in, such as push('Details', { transition: 'fade' }): see Transitions.

The way back​

The user goes back with the platform's controls, and they work without code:

PlatformBack
iOSThe back button of the top bar, and the swipe from the leading edge.
AndroidThe back arrow of the top bar, the back gesture and the system back.

When a screen leaves its stack by any of them, or by pop, it gets the event screenPopped before it unmounts. It is the place for what should happen when the user goes back, such as keeping a draft:

import { useScreenEvent } from '@apexnative/navigation';

useScreenEvent('screenPopped', () => {
saveDraft(text); // your own step
});

A custom back button, a swipe turned off, a screen that asks before the user leaves: see Back and gestures.

A deep link opens a screen deep in the app. The screens the user would have passed on the way go under it, so back works as if they came by hand. setStack builds that stack in one command:

await navigation.setStack(
'Inbox',
Screen('Chat', { userId }),
Screen('Profile', { userId }),
);

Profile shows; back goes to Chat, then to Inbox. Every screen of the new stack is new, so a link carries what each screen needs in its props. A link that lands in another tab or in another root builds the whole root: see A root made on the go and Deep links.

A full app with a list and its details, step by step: List and details.

API​