Starting the app
What createNavigation takes, what runs before and after the first screen, and what happens when the app launches again or fails to start.
Every app starts from one place, createNavigation. It describes the whole app in one object: its screens, what shows first, how the screens look, the data every screen reads, and what runs at launch. index.js registers it, and the app starts from it:
import { createNavigation } from '@apexnative/navigation';
import { HomeScreen } from './screens/Home';
export const Navigation = createNavigation({
screens: { Home: HomeScreen },
root: 'Home',
});
import { AppRegistry } from '@apexnative/navigation';
import { name } from './app.json';
import { Navigation } from './Navigation';
AppRegistry.registerComponent(name, Navigation);
The CLI's setup writes both files: see Installation. Only screens and root are needed; every other key is optional.
This page answers:
- What createNavigation takes: each key in short, with a link to its section.
- What runs before and after the first screen, and in what order.
- What happens when the app launches again, fails to start or is opened by a link.
What createNavigation takes
The screens and the first root
Screens
screens — the screens of the app by name. A command names a screen by its name, and the props it gives are typed from the component. groups gathers screens that share options, such as the screens of signing in, without a top bar.
screens: { Home: HomeScreen, Profile: ProfileScreen },
groups: {
auth: {
options: (s) => s.hideTopBar(),
screens: { Login: LoginScreen },
},
},
More: Screens and props.
The first root
root — what takes the window first. It is a screen name or a layout, as in the Quick start, or a function of the context, as willMount left it:
root: (context) => (context.signedIn ? 'Home' : 'Login'),
An app that switches between whole layouts, such as signed out and signed in, names them in roots instead, and picks the first with initialRoot. See Switching roots.
Data and launch
The app's data
context — the app's own data that every screen reads, such as whether the user is signed in. This key is its first value. Screens read it with useAppContext; root, initialRoot and didMount get it too. navigation.setContext is the one way to change it.
context: { signedIn: false },
// in any screen
const signedIn = useAppContext((context) => context.signedIn);
More: Theme and context.
At launch
willMount runs before the first screen, while the splash screen shows: the place to load what the first screen needs. didMount runs once the first screen shows: the place for what can wait. Both run once per JavaScript runtime, not once per launch: see When the app launches again.
splash adds a splash of your own in React Native, such as an animation of the logo, shown after the native splash on a cold start while willMount runs:
// Splash: your own component
splash: { component: Splash, minDuration: 800 },
In detail: Before the first screen, After the first screen and The splash screen.
How the screens look and show
How the screens look
options — the default options of every screen: what all screens share. A screen's own options win over them. theme gives the colors of the bars, the tabs and the backgrounds; navigation.setTheme changes it while the app runs.
options: (s) => s.largeTitle(false),
theme: {
colors: { primary: '#0A84FF', background: '#FFFFFF' },
},
More: Screen options, Theme and context.
How the screens show
presentation — what every push, modal and overlay starts from; the settings of a command win over it. Here every modal gets a close button:
presentation: {
modal: { closeButton: true },
},
More: Moving between screens.
Across the app
Events
listeners — handlers of the events of the navigation for the whole app, outside any screen: for example, every screen that appears, for analytics, or every change of the state, for a log.
listeners: {
// your own analytics
screenDidAppear: ({ screen }) => trackScreen(screen.name),
},
More: Lifecycle and events.
Links
linking — the links that open the app's screens, by path. Here myapp://user/42 pushes Profile with { userId: '42' }:
linking: {
prefixes: ['myapp://'],
routes: { 'user/:userId': 'Profile' },
},
More: Opened by a link below, and Deep links.
Providers
appProvider wraps the whole app once: every screen then renders in one React tree inside it, so a store or a query client is shared and lives as long as the app. screenProvider wraps each screen on its own.
// your own store
appProvider: ({ children }) => (
<StoreProvider store={store}>{children}</StoreProvider>
),
More: One tree.
Errors and loading
errorFallback is what a screen that failed to render shows instead, with retry; when the start fails, it is the first screen. loadingFallback shows while a screen waits for a lazy component or data. onError gets every error the navigation catches, with where it came from.
errorFallback: ({ error, retry }) => (
<ErrorView message={String(error)} onRetry={retry} />
),
loadingFallback: () => <ActivityIndicator />,
onError: (error, source) => reportError(error, source.type),
More: Errors and loading.
Rarely needed
Keys of the system; the defaults suit most apps.
safeArea— the edges on which every screen keeps its content out of the safe area:{ edges: ['top', 'left', 'right', 'bottom'] }. Listed edges replace the default, so list every edge the screens keep. Without it the screens keep the top and the sides, and the bottom is free.window— iOS the color behind the screens, seen around a modal as it comes in:{ backgroundColor: '#000000' }.direction—'ltr','rtl', or'locale'for the language of the app;navigation.setDirectionchanges it.
More: System UI.
Before the first screen
willMount is the place for what the first screen needs, such as whether the user is signed in. It may be async; the splash screen stays until it is done, so the first screen never shows half-ready. What it finds goes into the app's context with navigation.setContext, and root reads the context to choose the first screen:
import { createNavigation, navigation } from '@apexnative/navigation';
import { HomeScreen } from './screens/Home';
import { LoginScreen } from './screens/Login';
export const Navigation = createNavigation({
screens: { Login: LoginScreen, Home: HomeScreen },
context: { signedIn: false },
willMount: async () => {
// your own check, for example a saved token
const signedIn = await isSignedIn();
navigation.setContext({ signedIn });
},
root: (context) => (context.signedIn ? 'Home' : 'Login'),
});
navigation is the app's navigation for code outside the screens, such as willMount; a screen takes its own with useNavigation(). See Moving between screens.
Keep willMount short: it is the time the user looks at the splash screen. Load what the first screen needs, and leave the rest to the screens or to didMount.
The context is set in one way only, setContext: here in willMount, later when the user signs in or out. See The context.
The splash screen
The splash screen that covers willMount is the platform's own: the launch screen of iOS, a storyboard, and the splash screen of Android, set by the activity's theme. The system shows it before any JavaScript runs, so it is a picture in the native files: npx apex-navigation splash makes it from your logo and colors. The navigation keeps it on screen until the first root is drawn, so no empty window shows between the two.
An app that wants motion, such as a logo that grows into the first screen, or a progress while willMount loads, adds a splash of its own with splash. Its component takes the native splash's place on a cold start, with the same picture, and stays while willMount runs; then the first root comes in over it with a fade:
export const Navigation = createNavigation({
// …
splash: { component: Splash, minDuration: 800 },
});
It shows once per cold start of the app's process: not after a reload of the JavaScript, and not when Android gives the app a new activity (see below). It renders before willMount ends, so it is a React root of its own, outside appProvider and screenProvider. How to make both, with an animation that starts from the native logo: Splash screen.
After the first screen
didMount runs once the first root is on screen, with the same context. It is the place for what should happen when the user already sees the app, not over the splash screen: a permission request, a prompt to update, work that can wait.
export const Navigation = createNavigation({
// …
didMount: (context) => {
// your own step: a system dialog over the first screen
if (context.signedIn) askForNotifications();
},
});
Like willMount, it runs once per JavaScript runtime: not again when Android gives the app a new activity (see below).
The order
What runs in each kind of launch:
Splash screenThe OS starts the appwillMountRuns; the splash screen waitsFirst screenThe first root shows: the user sees the appdidMountRuns, with the first screen on screenSplash screenDoesn't show: a blank background for a momentwillMountRuns againFirst screenShows again, from the first rootdidMountRuns againSplash screenShows for a momentwillMountDoesn't runFirst screenThe last root comes back; screens pushed after it are gonedidMountDoesn't runSplash screenThe OS starts the appwillMountThrows; the error goes to onErrorFirst screenerrorFallback shows, with retrydidMountDoesn't run until a start succeedsWhen the app launches again
The native side can launch the app again while the JavaScript runtime keeps running. The navigation tells the cases apart, so willMount and didMount run once per runtime, not once per launch.
| Launch | willMount | splash | The first root | didMount |
|---|---|---|---|---|
| Cold start | runs | shows after the native splash, while willMount runs | built and shown, in place of the splash screen | runs |
| JavaScript reload (development) | runs again: a new runtime | doesn't show | built and shown; no splash screen, a blank background for a moment | runs again |
| Android A new activity, same runtime | doesn't run | doesn't show | the last root comes back, without the screens pushed or presented over it | doesn't run |
A cold start is the first launch of the app's process: the user taps the icon of an app that isn't running, also after the system ended its process in the background.
On Android the system can destroy the activity and create a new one while the app's process lives, for example after the user leaves the app for a long time. The app doesn't start over: the navigation sets the last root again, as root or the last switchTo made it, with the props of its screens.
When the start fails
If willMount throws, or the first root can't be built, the start fails. The error goes to onError with source.type === 'start', or to the console without it. What the user sees depends on errorFallback:
- With
errorFallback, it shows as the first screen, in place of the splash screen (the native one, orsplash), and itsretryruns the start again fromwillMount, withoutsplash. - Without it, the splash screen stays, and the next launch tries again.
So an app whose willMount can fail, such as without network, sets errorFallback. See When the start fails.
export const Navigation = createNavigation({
// …
onError: (error, source) => {
// your own crash reporting
if (source.type === 'start') reportError(error);
},
});
Opened by a link
A link can launch the app: myapp://user/42, tapped in a message. The routes of linking say what each link opens. A link waits for willMount, so the screens it opens find the context ready. With start it makes the first root; without it, the usual first root shows, and open runs after didMount.
import { Screen, Stack } from '@apexnative/navigation';
export const Navigation = createNavigation({
// …
linking: {
routes: {
'user/:userId': {
start: ({ userId }) =>
Stack('Home', Screen('Profile', { userId })),
open: ({ userId }) =>
navigation.push('Profile', { props: { userId } }),
},
},
},
});
Launched by myapp://user/42, the app shows Profile, with Home to go back to, as the splash screen goes. In a running app the same link pushes Profile.
- When the start fails, the links wait for the start that succeeds.
- A reload of the JavaScript doesn't run the link that launched the app again.
The routes, links in a running app, and links that wait for sign-in (usePendingLink): Deep links.
Next: screens and props
The app starts with its first root. Screens and props shows what a screen is, how it gets its data, and how it lives.