Create a route
Creating a screen with Solito is simple. There are 4 steps:
- Create a screen component
WebExport the component from a Next.js pageNativeRender the component in your React Navigation navigator of choiceNativeAdd the screen to your React Navigationlinkingconfig
What is React Navigation?
React Navigation handles the navigation on the iOS and Android apps. Its API is similar to React Router.
Starter​
To see a full example, I recommend referencing the solito starter. You can see the full code here.
This guide will assume you're editing the starter app.
Video​
For a high-level overview of creating a screen for React Native and Next.js, you can reference the navigation section of Fernando Rojo's Next.js talk:
Guide​
1. Create a screen​
Create a file at packages/app/features/settings/screen.tsx:
tsximport {View ,StyleSheet ,Text } from 'react-native'Âexport functionSettingsScreen () {return (<View style ={styles .container }><Text style ={styles .text }>Settings</Text ></View >)}Âconststyles =StyleSheet .create ({container : {flex : 1,alignItems : 'center',justifyContent : 'center',backgroundColor : '#111',},text : {color : '#fff',},})
tsximport {View ,StyleSheet ,Text } from 'react-native'Âexport functionSettingsScreen () {return (<View style ={styles .container }><Text style ={styles .text }>Settings</Text ></View >)}Âconststyles =StyleSheet .create ({container : {flex : 1,alignItems : 'center',justifyContent : 'center',backgroundColor : '#111',},text : {color : '#fff',},})
Here we have a basic SettingsScreen component. Since it's inside of packages/app, both the native app and website can use it.
This is a common pattern for Solito; created shared UI inside of packages/, and then render them in their respective apps.
2. Next.js page​
In apps/next/pages/settings.tsx, export the screen as default:
tsxexport { SettingsScreen as default } from 'app/features/settings/screen'
tsxexport { SettingsScreen as default } from 'app/features/settings/screen'
Start the web app and open localhost:3000/settings to see your settings screen!
3. React Navigation​
Now that the SettingsScreen works on the website, let's add it to the native app.
In your solito starter, open packages/app/navigation/native/index.tsx.
It should look like this
tsximport { createNativeStackNavigator } from '@react-navigation/native-stack'Âimport { HomeScreen } from '../../features/home/screen'import { UserDetailScreen } from '../../features/user/detail-screen'Âconst Stack = createNativeStackNavigator<{home: undefined'user-detail': {id: stri ng} }>()Âexport function NativeNavigation() {return (<Stack.Navigator><Stack.Screenname="home"component={HomeScreen}options={{title: 'Home',}}/><Stack.Screenname="user-detail"comp onent={UserDetailScreen}opti ons={{title: 'User',}}/></Stack.Navigator>)}
tsximport { createNativeStackNavigator } from '@react-navigation/native-stack'Âimport { HomeScreen } from '../../features/home/screen'import { UserDetailScreen } from '../../features/user/detail-screen'Âconst Stack = createNativeStackNavigator<{home: undefined'user-detail': {id: stri ng} }>()Âexport function NativeNavigation() {return (<Stack.Navigator><Stack.Screenname="home"component={HomeScreen}options={{title: 'Home',}}/><Stack.Screenname="user-detail"comp onent={UserDetailScreen}opti ons={{title: 'User',}}/></Stack.Navigator>)}
Render the screen​
tsximport { createNativeStackNavigator } from '@react-navigation/native-stack'Âimport { HomeScreen } from '../../features/home/screen'import { UserDetailScreen } from '../../features/user/detail-screen'import { SettingsScreen } from '../../features/settings/screen'Âconst Stack = createNativeStackNavigator<{home: undefined'user-detail': {id:st ngri } settings: undefined // this screen has no params}>()Âexport function NativeNavigation() {return (<Stack.Navigator><Stack.Screenname="home"component={HomeScreen}options={{title: 'Home',}}/><Stack.Screenname ="settings"component={Setting sScreen}options={{title: 'Settings', }}/><Stack.Screenname="user-detail"component={UserDetailScreen}option s={{title: 'User', }} /></Stack.Navigator >)}
tsximport { createNativeStackNavigator } from '@react-navigation/native-stack'Âimport { HomeScreen } from '../../features/home/screen'import { UserDetailScreen } from '../../features/user/detail-screen'import { SettingsScreen } from '../../features/settings/screen'Âconst Stack = createNativeStackNavigator<{home: undefined'user-detail': {id:st ngri } settings: undefined // this screen has no params}>()Âexport function NativeNavigation() {return (<Stack.Navigator><Stack.Screenname="home"component={HomeScreen}options={{title: 'Home',}}/><Stack.Screenname="settings"component={Setting sScreen}options={{title: 'Settings', }}/><Stack.Screenname="user-detail"component={UserDetailScreen}options={{title: 'User', }} /></Stack.Navigator >)}
Here we added a Stack.Screen as a child of <Stack.Navigator>.
The name of the screen is settings. This name is important and will be used in step 4 when we map a URL to a screen name.
For the component prop, we pass the same SettingsScreen component we used for the website.
4. Configure native linking​
To map a URL to a screen on the native app, we'll use React Navigation's linking feature.
Add the URL to the config​
Open packages/app/provider/navigation/index.tsx.
Inside the linking prop, point the settings screen name to the /settings URL path.
tsximport { NavigationContainer } from '@react-navigation/native'import * as Linking from 'expo-linking'import { useMemo } from 'react'export function NavigationProvider({children,}: {children: React.ReactNode}) {return (<NavigationContainerlinking={useMemo(() => ({prefixes: [// ...add your URLs hereLinking.createURL('/'),],config: {initialRouteName: 'home',screens: {home: '','user-detail': 'user/:id',settings: 'settings',},},}),[])}>{children}</NavigationContainer>)}
tsximport { NavigationContainer } from '@react-navigation/native'import * as Linking from 'expo-linking'import { useMemo } from 'react'export function NavigationProvider({children,}: {children: React.ReactNode}) {return (<NavigationContainerlinking={useMemo(() => ({prefixes: [// ...add your URLs hereLinking.createURL('/'),],config: {initialRouteName: 'home',screens: {home: '','user-detail': 'user/:id',settings: 'settings',},},}),[])}>{children}</NavigationContainer>)}
In the linking.config.screens field, the key is the React Navigation screen name, and the value is the URL path. For example, if we wanted the URL for the settings screen to be /preferences, we could do this instead:
json{"screens": {"home": "","user-detail": "user/:id","settings": "preferences"}}
json{"screens": {"home": "","user-detail": "user/:id","settings": "preferences"}}
To repeat: the value (i.e. preferences) is the URL path. It just ignores the leading slash.
did you know?
The Expo team is working on bringing filesystem routing to React Native, similar to Next.js and Remix.
5. Bonus​
Now we can link to the settings screen from any shared component!
tsximport {TextLink } from 'solito/link'Âexport functionLinkToSettings () {return <TextLink href ="/settings">Settings</TextLink >}
tsximport {TextLink } from 'solito/link'Âexport functionLinkToSettings () {return <TextLink href ="/settings">Settings</TextLink >}
You can also use router.push:
tsximport {useRouter } from 'solito/router'Âexport functionLinkToSettings () {constrouter =useRouter ()ÂfunctionlinkToSettings () {router .push ('/settings')}Âreturn <Form onSuccess ={linkToSettings } />}
tsximport {useRouter } from 'solito/router'Âexport functionLinkToSettings () {constrouter =useRouter ()ÂfunctionlinkToSettings () {router .push ('/settings')}Âreturn <Form onSuccess ={linkToSettings } />}