Simple and performant navigation on iOS, Android and the web with simple and type-safe api.
Build on top of grahammendick/navigation. Check it out if you want more control over the navigation!
- New architecture
- Simple api
- 100% Type safety (routes, params, bottom tabs)
- Bundle splitting (lazy loading, smart prefetching)
- Render as you fetch on iOS, Android & web
- Works on Android, iOS & web!
- Preload data on mouseDown (or more sensitive on hover, see
<Link />example) - Preload component on hover (on web)
- Automatic deep-linking
- Real-time light/dark mode
- Out of the box scroll restoration (on web), because screens in tab are pushed on top of each other!
- A lot of control over the web layout
- Universal links (already works, but docs need work)
- PWA documentation (already works, but docs need work)
- Expo support
- Metro bundling on web
If you use bundling with metro add this to your index.js file
import '@expo/metro-runtime';Customize index.html by pressing space on the web/index.html item + enter
npx expo customize:web Change in your index.html in body css overflow-y:hidden to overflow: hidden Because otherwise scrolling works very bad on iOS safari.
This is an older video which used react-native-navigation so we have newer material you bottom bar in the meantime :) View video in better frame on YouTube
We want developers to be able to build software faster using modern tools like GraphQL, Golang and React Native.
Give us a follow on Twitter: RichardLindhout, web_ridge
See example: reactnativeridgenavigation.com
You can register screens with a preload function, the params will be automatically in string format based on the url.
// NavigatorRoutes.ts const PostScreen = registerScreen( '/post/:id', lazy(() => import('./PostScreen'), ({ id }) => { queryClient.prefetchQuery( queryKeyPostScreen({ id }), queryKeyPostScreenPromise({ id }), { staleTime: 3000 } ); // if you return something here it can be used in the screen itself or somewhere else with // usePreloadResult(routes.PostScreen) // in this case react-query handles it based on queryKey so it's not needed but with Relay.dev it is. // you can put the result of the usePreloadResult in your usePreloadedQuery if you use Relay.dev } ); const routes = { PostScreen, // ... } export default routes- normal
- bottomTabs
every screen can be used within every stack. You don't have to configure screens for a stack.
Use Expo with prebuild (Expo Go is not supported since we have native libraries)
yarn add react-native-ridge-navigation navigation-react-native or with npm
npm install react-native-ridge-navigation navigation-react-native Support for Material You bottom bar in Android
const { withAndroidStyles } = require("@expo/config-plugins"); module.exports = function androidMaterialYouBottomBarPlugin(config) { return withAndroidStyles(config, async (config) => { const styleFile = config.modResults; const appTheme = styleFile.resources.style.find( (style) => style.$.name === 'AppTheme', ); appTheme.$.parent = 'Theme.Material3.DayNight.NoActionBar'; return config; }); };"plugins": [ "./expo-plugin-android-material-you-bottom-bar" ]NavigationRoots.tsx
const NavigationRoots = { RootHome: 'home', RootAuth: 'auth', }; export default NavigationRoots;BottomRoots.tsx
// svg to png // https://webkul.github.io/myscale/ // // tab icon // http://nsimage.brosteins.com/Home const BottomRoots = { Posts: { path: '/post', title: () => 'Posts', icon: require('./img/palette-swatch-outline/icon-20.png'), selectedIcon: require('./img/palette-swatch/icon-20.png'), child: routes.PostsScreen, }, Account: { path: '/account', title: () => 'Account', icon: require('./img/account-circle-outline/icon-20.png'), selectedIcon: require('./img/account-circle/icon-20.png'), child: routes.AccountScreen, }, }; export default BottomRoots;// App.tsx import * as React from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import AsyncBoundary from './helpers/AsyncBoundary'; import { createBottomTabsRoot, createNormalRoot, NavigationProvider, } from 'react-native-ridge-navigation'; import { BottomRoot, NavigationRoots, screens } from './Navigator'; import routes from './Routes'; import AsyncBoundaryScreen from './helpers/AsyncBoundaryScreen'; const navigationRoot = { [NavigationRoots.RootHome]: createBottomTabsRoot( [BottomRoot.Home, BottomRoot.Posts, BottomRoot.Account], // if you want to override web layout // { // breakingPointWidth: 500, // components: { // override: WebLayout, // }, // } ), [NavigationRoots.RootAuth]: createNormalRoot(routes.AuthScreen), }; export default function App() { return ( <SafeAreaProvider> <NavigationProvider screens={screens} SuspenseContainer={AsyncBoundaryScreen} navigationRoot={navigationRoot} /> </SafeAreaProvider> ); }See example code for the async-boundary stuff :)
Use the <Link /> component as much as possible since it will work with ctrl+click on the web :)
<Link to={routes.PostScreen} params={{ id: `${item.id}` }} // linkMode="default" // optional if sensitive the preload will be called on hover instead of mouseDown > {(linkProps) => ( <Pressable {...linkProps}> // or other touchables/buttons <Text>go to post</Text> </Pressable> )} </Link> Use the createLinkComponent component to create re-usable links without render props. E.g to create a linkable button for react-native-paper
//ButtonLink.tsx import { Button } from 'react-native-paper'; import { createLinkComponent } from 'react-native-ridge-navigation'; const ButtonLink = createLinkComponent(Button); export default ButtonLink;<ButtonLink to={routes.PostScreen} params={{ id: '2' }} mode="contained" // all optional RNP props // all optional Link props > Go further </ButtonLink>Alternatives (push, replace, refresh or fluent)
const { push, replace, refresh, fluent } = useNavigation(); // call this where if you can't use <Link /> push(routes.PostScreen, { id: `${item.id}` }); // call this if e.g. after a create you want to go to edit screen // but without pushing history to the url-stack or app-stack :) replace(routes.PostScreen, { id: `${item.id}` }); // call this if you want to refresh the screen with new params refresh(routes.PostScreen, { id: `${item.id}` }); // normal root, replace full history fluent( fluentRootNormal(NavigationRoots.RootAuth), fluentScreen(Routes.HomeScreen, {}), fluentScreen(Routes.PostScreen, { id: '10' }), fluentScreen(Routes.PostScreen, { id: '20' }), fluentScreen(Routes.PostScreen, { id: '30' }) ); // bottom root, replace full history fluent( fluentRootBottomTabs(NavigationRoots.RootHome, BottomRoot.Account), fluentScreen(Routes.HomeScreen, {}), fluentScreen(Routes.PostScreen, { id: '10' }), fluentScreen(Routes.PostScreen, { id: '20' }), fluentScreen(Routes.PostScreen, { id: '30' }) );Switch root can be used to switch from e.g. the auth screen to a different entrypoint of your app. E.g. check the role and switch the stacks to different roots for different user roles.
<SwitchRoot rootKey={NavigationRoots.RootHome} params={{}} />; // or e.g. <SwitchRoot rootKey={NavigationRoots.RootAuth} params={{}} />;All available properties
const { pop, // go back switchRoot, preload, // call preload (done automatic on link mouseDown push, // calls preload + pushes screen replace, // calls preload + replaces screen refresh, // calls preload + replaces params of screen } = useNavigation() const { switchToTab, currentTab } = useBottomTabIndex(); const { updateBadge, badges } = useBottomTabBadges(); // updateBadge(BottomRoots.Projects, '10'); // switchToTab(BottomRoot.Posts);If you want a nested stack e.g. in a modal, check out the example code .
import { NavigationNestedProvider, ModalBackHandler, } from 'react-native-ridge-navigation'; <ModalBackHandler> {(handleBack) => ( <Modal visible={modalVisible} style={{ backgroundColor: theme.colors.background }} statusBarTranslucent={true} presentationStyle="pageSheet" animationType="slide" onRequestClose={() => { if (!handleBack()) setModalVisible(false); }} > <NavigationNestedProvider> {/* you can render your children here and push to all registered screens*/} <View style={{ height: 250, backgroundColor: 'pink' }}> <Header title="Modal stack" /> <Button onPress={onClose}>Close modal</Button> <Link to={routes.PostScreen} params={{ id: '2' }}> {(linkProps) => <Button {...linkProps}>Account</Button>} </Link> </View> </NavigationNestedProvider> </Modal> )} </ModalBackHandler>You have to enable url schemes etc in your app and it'll work!
If you want to deep link into a bottom tab or other screen you can use the fluent navigation in development mode and it will log the required url to go to the right screen :)
// global createLinkComponent, SwitchRoot, BottomTabLink, Link BackLink // for now .pop() but we'll update this according to Android guidelines later on (to always go back in hierarchy) lazyWithPreload // only available on the web: see example app Redirect, NavigationRoot, createNavigation, createBottomTabsRoot, createNormalRoot, registerScreen, createScreens, defaultTheme, setTheme, getTheme, createSimpleTheme, setPreloadResult, // should not need this as it is done automatically // common hooks useParams, useNavigation, // extra useBottomTabIndex, useBottomTabBadges, useBottomTabRefresh, useTheme, useIsFocused, useFocus,// same as useNavigating useNavigating, useNavigated, useUnloaded, usePreloadResult // e.g. usePreloadResult(routes.PostScreen)If you want to use a component globally with the navigation context pass them as children of the NavigationProvider
<NavigationProvider {...}> <UpdateBottomTabBadgeSubscriber /> </NavigationProvider>On the web version we need to disable window scroll since else it will sometimes use windows scroll instead of the scrollview Add this to your css
body { overflow: hidden; }See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
-
Simple form library for React Native with great UX for developer and end-user react-native-use-form
-
Smooth and fast cross platform Material Design date and time picker for React Native Paper: react-native-paper-dates
-
Smooth and fast cross platform Material Design Tabs for React Native Paper: react-native-paper-tabs
-
Simple translations in React ( Native): react-ridge-translations
-
Simple global state management in React (Native): react-ridge-state
