• Stars
    star
    107
  • Rank 323,587 (Top 7 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 3 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple, performant & type-safe cross platform navigation in React Native / React Native Web


react-native-ridge-navigation

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!

Features

  • New architecture (Fabric) ready
  • 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)

Example

Demo of react-native-ridge-navigation 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

About us

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

Demo

See example: reactnativeridgenavigation.com

Source

See source of example app

Register screens

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

Supported stacks

  • normal
  • bottomTabs

every screen can be used within every stack. You don't have to configure screens for a stack.

Installation

1. If you don't have an app yet (optional, but recommended)

Use Expo with prebuild (Expo Go is not supported since we have native libraries)

2. Install deps + library

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

1. add file expo-plugin-android-material-you-bottom-bar.js to your root folder

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;
  });
};

2. add this to your expo config app.config.js or app.json

"plugins": [
"./expo-plugin-android-material-you-bottom-bar"
]

Usage

import {
  createNormalRoot,
  createNavigation,
  createBottomTabsRoot,
  createScreens,
  defaultTheme,
} from 'react-native-ridge-navigation';

export const NavigationRoots = {
  RootHome: 'home',
  RootAuth: 'auth',
};

// svg to png
// https://webkul.github.io/myscale/
//
// tab icon
// http://nsimage.brosteins.com/Home
export 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,
  },
};
// 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],
    {
      breakingPointWidth: 500,
      components: {
        override: HeaderWeb,
      },
    }
  ),
  [NavigationRoots.RootAuth]: createNormalRoot(routes.AuthScreen),
};

export default function App() {
  return (
    <SafeAreaProvider>
      <NavigationProvider
        screens={screens}
        SuspenseContainer={AsyncBoundaryScreen}
        navigationRoot={navigationRoot}
      />
    </SafeAreaProvider>
  );
}

See example code for the asyncboundary stuff :)

New screen

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>

createLinkComponent

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

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={{}} />;

useNavigation

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()

Control bottom tabs

  const { switchToTab, currentTab } = useBottomTabIndex();
const { updateBadge, badges } = useBottomTabBadges();
// updateBadge(BottomRoots.Projects, '10');
// switchToTab(BottomRoot.Posts);

Modal stack

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>

Deep linking

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 :)

More

  // 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,
  StatusBar,
  FocusAwareStatusBar,
  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)

Do global stuff like updating badge

If you want to use a component globally with the navigation context pass them as children of the NavigationProvider

<NavigationProvider {...}>
  <UpdateBottomTabBadgeSubscriber />
</NavigationProvider>

Scroll fix on web version

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;
}

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT

Checkout our other libraries

More Repositories

1

react-native-paper-dates

Smooth and fast cross platform Material Design date and time picker for React Native Paper
TypeScript
607
star
2

react-ridge-state

Simple 💪 fast ⚡️ and small 🎈 (400 bytes) global state management for React (Native)
TypeScript
223
star
3

react-native-paper-tabs

Smooth and fast cross platform Material Design Tabs for React Native Paper
TypeScript
179
star
4

react-native-use-form

Simple form library for React Native with great UX for developer and end-user
TypeScript
81
star
5

gqlgen-sqlboiler

This is a plugin for gqlgen to generate converts + filter queries and resolvers for sqlboiler
Go
74
star
6

react-ridge-translations

Simple 💪 fast ⚡️ and small 🎈 (400 bytes) translation library for React / React Native
TypeScript
65
star
7

create-react-native-web-application

A simple starting point for creating iOS, Android en webapp in 1 codebase with React Native (Web)
JavaScript
41
star
8

react-native-paper-autocomplete

The autocomplete package you wished for on all platforms (iOS, Android, web)
TypeScript
17
star
9

react-native-web-hover

Some enhanced elements for react-native-web to support hover on the web.
TypeScript
16
star
10

contact-tracing

A privacy-first contact tracing solution for Android and iOS where users have full control over their data without the Google or Apple Contact Tracing API's
TypeScript
13
star
11

react-native-ridge-list

FlatList abstraction which uses react-window on the web to create better list performance
TypeScript
9
star
12

gqlgen-sqlboiler-examples

Examples of https://github.com/web-ridge/gqlgen-sqlboiler
Go
8
star
13

sqlboiler-graphql-schema

MOVED TO https://github.com/web-ridge/gqlgen-sqlboiler
Go
7
star
14

email-reply-parser

Parses a plain text email and returns it without signatures and other things
Go
6
star
15

detox-web

Abstraction on top of puppeteer to support Detox test to run on the web platform
Java
4
star
16

dbifier

Aims to create a tool to which is easy to use to model your database
TypeScript
3
star
17

utils-go

Go
2
star
18

rem-to-px

rem-to-px cli program which you can put in your package.json. It replaces rem with px.
JavaScript
2
star
19

ridgecontent

[Future plans] Modern CMS system and very simple for end-user and developer
1
star
20

graphql-schema-react-native-app

The goal for this repository is to generate a fully working CRUD app based on an graphql schema with using Relay.dev (experimental)
1
star