react-native-easy-navigation

Simple and fast navigation for react-native

Usage no npm install needed!

<script type="module">
  import reactNativeEasyNavigation from 'https://cdn.skypack.dev/react-native-easy-navigation';
</script>

README

react-native-easy-navigation

Simple and fast navigation for react-native

Note: Needs react-native-reanimated and react-native-gesture-handler!

Table of Contents

Why this instead of react-navigation or wix-navigation?

Well, in most cases I guess the best option this to choose one of above but I created this lib so I could create apps faster with less worry about the navigation part. This lib is super flexible. The only required component to use is NavigationProvider. The rest is just there for getting started faster.

Features

  • Supports left-to-right transitions for ios and android
  • Same transitions on both ios and android
  • Drawer support
  • Tabs support
  • Supports push, replace and pop screens
  • Regular screens
  • Half panels
  • Modals
  • Layovers

Installation

Follow the instructions how to install react-native-gesture-handler and react-native-reanimated. Nothing else is required. Read Usage to get started using the navigation.

Usage

const Home = () => {
  const router = useRouter()
  return (
    <Screen title="Home">
      <Button onPress={() => router.navigateToArticle({ title: 'Article 1', id: 1 })} title="Go to Article 1"/>
      <Button onPress={() => router.navigateToArticle({ title: 'Article 2', id: 2 })} title="Go to Article 2"/>
    </Screen>
  )
}

const Profile = () => {
  const router = useRouter()
  return (
    <Screen title="Profile">
      <Button onPress={() => router.navigateToMyModal({ text: 'Modal: This is a prop passed to the Modal screen' })} title="Open modal"/>
      <Button onPress={() => router.navigateToMyHalfPanel({ text: 'HalfPanel: This is a prop passed to the Modal screen' })} title="Open half panel"/>
    </Screen>
  )
}

const Article = ({ title, id }) => {
  const router = useRouter()
  return (
    <Screen title={title || 'Article'}>
      <Text>Fetch article with ID = {id}</Text>
      <Button onPress={router.pop} title="Custom go back button"/>
      <Button onPress={() => router.replace('Article', { props: { title: 'Article 3 - Replaced', id: 3 } })} title="Replace with Article 3"/>
      <Button onPress={() => router.push('Article', { props: { title: 'Article 4 - Push', id: 3 } })} title="Push with Article 4"/>
      <Button onPress={() => router.push('Article', { props: { title: 'Article 4 - Push', id: 3 } })} title="Push with Article 4"/>
    </Screen>
  )
}

const Modal = ({ text, type }) => {
  return (
    <Screen title={type}>
      <Text>{text}</Text>
    </Screen>
  )
}

const Drawer = ({ text, type }) => {
  return (
    <View style={{ flex: 1 }}>
      <Text>My drawer</Text>
    </View>
  )
}

const App = () => {
  return (
    <NavigationProvider 
      initial="Home"
      routes={{
        Home: { Component: Home },
        Profile: {
          Component: Profile, 
          statusBar: {
            barStyle: 'light-content',
          },
          header: {
            backgroundColor: '#000',
            color: '#fff'
          }
        },
        Article: { Component: Article },
        Drawer: { Component: Drawer },
      }}
      router={{
        // Creates helper functions that we can get from `useRouter`-hook
        // The params to the functions will be passed as props to the component
        // Ex. `navigateToArticle({ id: 1, title: 'My article' })`
        navigateToHome: { name: 'Home' },
        navigateToProfile: { name: 'Profile' },
        navigateToArticle: { name: 'Article' },
        navigateToMyModal: { name: 'Modal' }
        navigateToMyHalfPanel: { name: 'Modal' },
        openDrawer: { name: 'Drawer', mode: 'drawer' },
      }}
      renderTab={(route) => {
        // Will be called for each route
        // If current route is Article we do not want to display the tabs
        if (route.name === 'Article') {
          return null
        }

        // You can also create an array of all your tab items and then just map them.
        // Tabs and Tabs.Item is just some helper components,
        // You can easily create your own and then navigate to what ever you want
        return <Tabs>
          <Tabs.Item 
            route="Home" 
            icon={active => (
              <Ionicons
                name={icon}
                size={26}
                color={active ? "#007aff" : "black"}
              />
            )}
            label={active => (
              <Text style={{ color: active ? "#007aff" : "black" }}>
                Home
              </Text>
            )}
            badge={() => <Tabs.Badge number={3} />} 
          />
          <Tabs.Item 
            route="Home" 
            icon={active => (
              <Ionicons
                name={icon}
                size={26}
                color={active ? "#007aff" : "black"}
              />
            )}
            label={active => (
              <Text style={{ color: active ? "#007aff" : "black" }}>
                Home
              </Text>
            )}
            badge={() => <Tabs.Badge number={3} />} 
          />
        </Tabs>
      }}
    />
  )
}

Components

NavigationProvider

<NavigationProvider
  router={{
    // define shortcuts naviigation functions
    navigateToArticle: {
      name: 'Article'
    },
  }}
  routes={{
    Home: {
      name: 'Home',
      statusBar: {
        barStyle: 'light-content|dark-content'
      },
      mode: 'replace|push|drawer|overlay',
      // ignore header if you don't wrap your views with the <Screen> component
      header: {
        backgroundColor: '#000',
        color: '#fff',
      }
    },
    Article: {
      name: 'Article',
    },
  }}
  // renderTab gets called for each route
  // here you can do some conditional render
  renderTab={(route, router) => {
    if (route.name !== 'Home') {
      return null
    }
    
    return (
      <Tabs>
        <Tabs.Item 
          route="Home"
          icon={active => (<Icon name="home"/>)}
          label={active => <Text>Home</Text>)}
          badge={() => <Tabs.Badge number={3} />} 
        />
      </Tabs>
    )
  }}
>

Screen

You can wrap all your main views with the Screen component. This component will add a header with a title and a back button (if you are deeper then the first screen).

const Home = () => (
  <Screen title="Home">
    {/* rest of your view */}
  </Screen>
)

Hooks

useScreen

This is help full to get information from the screen you are at.

const Home = () => {
  const { 
    showBackButton, 
    screen: {
      id,
      name,
      mode,
      animated,
      statusBar: { barStyle },
      header: {
        backgroundColor,
        color,
      } 
    }
  } = useScreen()

  return null
}

useRouter

If you want to navigate this is the hook!

const Home = () => {
  const { 
    push,
    pop,
    replace,
    reset,
    // and all the shortcuts you defined in <NavigationProvider router={} />
    navigateToArticle,
  } = useRouter()

  return (
    <>
      <Button 
        title="Push" 
        onPress={() => push('Article', { 
          props: { id: 1 }, 
          mode: '', 
          statusBar: {}, 
          header: {}, 
          animated: true 
        })} 
      />
      <Button 
        title="Shortcut" 
        onPress={() => navigateToArticle({ id: 1 })}
      />
    </>
  )
}