• Stars
    star
    388
  • Rank 110,171 (Top 3 %)
  • Language
    TypeScript
  • Created almost 5 years ago
  • Updated over 1 year ago

Reviews

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

Repository Details

⛴ Make Next.js and react-navigation play nicely together with an Expo/React Native Web app.

🥳 This library has moved to solito!

Exciting news: the future of this library will be solito. I completely changed the API and approach into something much more scalable and simple, so I decided to make a new library altogether.

This change is a result of my thinking on #68 about the future API.

If you're already using expo-next-react-navigation, don't stress. You can continue to use it as you please. But I recommend using solito, even if you're incrementally adopting it.

I have tons of code using expo-next-react-navigation in my own app, and I won't be able to change it overnight. But going forward, all new navigation code I write will use solito instead.

While this repo will of course stay up, I will probably no longer make updates to expo-next-react-navigation.


Expo + Next.js Router + React Navigation 🥳

A set of hooks that wrap the react-navigation API that you're used to, and make it work with next/router.

This library helps me use the Expo + Next.js integration without stressing about navigation.

Next.js Conf

Screen Shot 2021-10-22 at 3 00 05 PM

I'm speaking at Next.js Conf 2021 on October 26 about React Native + Next.js. Get your ticket to see how we do it.

Example

👾 Github Repo | 💻 Website | 📱 Open expo app directly | ☎️ Expo app website

Install

For react-navigation v5 or v6:

yarn add expo-next-react-navigation

For react-navigation v4

Table of contents

Set up

Before continuing, I highly, highly recommend using this monorepo as your starter.

The steps below are copied from Expo's docs essentially.

However, the monorepo above is much more updated, and it works with Next.js 11, Webpack 5, and React Navigation v6 😎

If you use the monorepo, you don't need to do the setup below.


Step 0. Install next with expo:

  • Init: expo init (or npx create-next-app)

  • Install: yarn add @expo/next-adapter

  • Install next: yarn add next

  • Configure: yarn next-expo

  • Start: yarn next dev

I recommend becoming familiar next's architecture with expo. Follow the Expo docs or see this article by Evan Bacon if you're curious.

Step 1. Edit/create next.config.js

yarn add next-compose-plugins next-fonts next-images next-transpile-modules

Step 2: edit next.config.js to look something like this:

/* eslint-disable @typescript-eslint/no-var-requires */
const { withExpo } = require('@expo/next-adapter')
const withFonts = require('next-fonts')
const withImages = require('next-images')
const withPlugins = require('next-compose-plugins')

const withTM = require('next-transpile-modules')([
  'expo-next-react-navigation',
  // you can add other modules that need traspiling here
])

module.exports = withPlugins(
  [withTM, withFonts, withImages, [withExpo, { projectRoot: __dirname }]],
  {
    // ...
  }
)

All done! Run yarn next dev & open http://localhost:3000 👻

You can add other packages that need transpiling to the transpileModules array. See this post for details.

Usage

Replace the following instances in your code after installation and setup:

useNavigation 👉 useRouting

-import { useNavigation } from 'react-navigation-hooks'
+import { useRouting } from 'expo-next-react-navigation'

useLayoutEffect

-import { useLayoutEffect } from 'react-navigation-hooks'
+import { useLayoutEffect } from 'expo-next-react-navigation'

<TouchableOpacity /> 👉 <Link />

-import { TouchableOpacity } from 'react-native'
+import { Link } from 'expo-next-react-navigation'

-<TouchableOpacity onPress={() => navigate({ routeName: 'chat' })}>
-  <Text>Go</Text>
- </TouchableOpacity>
+<Link routeName="chat" params={{ roomId: 'hey!' }}>
+  Go
+</Link>

All set ⚡️

API

useRouting

React hook that wraps useNavigation (from react-navigation) hook and useRouter (from next-router).

It follows the same API as useNavigation.

import { useRouting } from 'expo-next-react-navigation'

export default function App() {
  const { navigate, push, getParam, goBack } = useRouting()


}

navigate

Only argument is a dictionary with these values. Unlike react-navigation, this doesn't currently support a string as argument.

  • routeName: string, required
  • params: optional dictionary
  • web: Optional dictionary with added values for web, following the API from next/router's Router.push function.
    • path: (optional) Fulfills the same value as pathname from next/router, overriding the routeName field. If you set this to /cars, it will navigate to /cars instead of the routeName field. As a result, it will load the file located at pages/cars.js.
    • as: (optional) If set, the browser will show this value in the address bar. Useful if you want to show a pretty/custom URL in the address bar that doesn't match the actual path. Unlike the path field, this does not affect which route you actually go to.
    • shallow: Update the path of the current page without rerunning getStaticProps, getServerSideProps or getInitialProps. Defaults to false

Example: Navigate to a user

export default function Home() {
  const { navigate } = useRouting()

  // goes to yourdomain.com/user?id=chris
  const onPress = () =>
    navigate({
      routeName: 'user',
      params: { id: 'chris' },
    })

  // 👇or this👇
  // goes to `yourdomain.com/user/chris`
  const navigateCleanLink = () =>
    navigate({
      routeName: 'user',
      params: { id: 'chris' },
      web: { as: `/user/chris` },
    })

  // 👇or this👇
  // 'profile' path overrides 'user' on web, so it uses the pages/profile.js file
  // even though it navigates to yourdomain.com/profile?id=chris?color=blue`
  // ...it actually shows up as yourdomain.com/@chris in the URL bar.
  const navigateCleanLinkWithParam = () =>
    navigate({
      routeName: 'user',
      params: { id: 'chris', color: 'blue' }, // accessed with getParam in the next screen
      web: { as: `/@chris`, path: 'profile' },
    })
}

This follows the next pattern of dynamic routing. You'll need to create a pages/user/[id].js file.

For more thoughts on how and when you should use the web field, see Web Thoughts.

getParam

Same API as getParam from react-navigation.

Similar to query from next/router, except that it's a function to grab the values.

pages/user/[id].js

Imagine you navigated to yourdomain.com/user/chris on web using the example above.

export default function User() {
  const { getParam } = useRouting()

  const id = getParam('id') // chris

  // do something with the id
}

useFocusEffect

See react navigation docs. On web, it simply replaces the focus effect with a normal effect hook. On mobile, it is the exact react navigation hook.

Make sure to use useCallback as seen in the example.

import { useFocusEffect } from 'expo-next-react-navigation'

export default ({ userId }) => {
  useFocusEffect(
    useCallback(() => {
      const unsubscribe = API.subscribe(userId, user => setUser(user))

      return () => unsubscribe()
    }, [userId])
  )

  return <Profile userId={userId} />
}

Link

The following will use the chat route in react navigation.

However, it will use the pages/room.js file for nextjs. Also, it will show up as domain.com/messages in the address bar.

Optionally accepts a nextLinkProps prop dictionary and touchableOpacityProps dictionary as well.

export default function Button() {
  return (
    <Link
      routeName="chat"
      params={{ roomId: '12' }}
      web={{
        path: '/room',
        as: 'messages',
      }}
    >
      Chat in room 12
    </Link>
  )
}

Required props:

Optional props

  • web: A dictionary with the follwing options:
type Web = {
  /**
   * Alternative path to override routeName on web.
   */
  path?: string
  /**
   * A custom URL ending to show in the browser address bar instead of the `web.path` or `routeName`.
   *
   * Should start with `/`.
   */
  as?: string
  /**
   * Prefetch the page in the background. Defaults to `true`
   */
  prefetch?: boolean
  /**
   * Scroll to the top of the page after a navigation. Defaults to `true`
   *
   */
  scroll?: boolean
  /**
   * Replace the current history state instead of adding a new url into the stack. Defaults to `false`
   */
  replace?: boolean
  /**
   * Update the path of the current page without rerunning getStaticProps, getServerSideProps or getInitialProps. Defaults to false
   */
  shallow?: boolean
}
  • web: dictionary, see useRouting().navigate docs. On v1.0.5+, you can also pass the prefetch, replace, and scroll booleans here, from the next/link component.

  • touchableOpacityProps: extends React Native's TouchableOpacity props.

  • nextLinkProps: extends next/router's Link props.

  • isText: if false, you can set the children to be non-Text nodes. Defaults to true. If true, the children can be a string or a Text node.

Other shout outs

nextjs-progressbar

I think this is an awesome package for adding a loading progress bar to your next pages. It's super easy. Check it out.

Link: https://www.npmjs.com/package/nextjs-progressbar

yarn add nextjs-progressbar

or

npm i nextjs-progressbar

pages/_app.js

import React from 'react'
import App from 'next/app'
import NextNprogress from 'nextjs-progressbar'

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props
    return (
      <>
        <NextNProgress
          color="#29D"
          startPosition="0.3"
          stopDelayMs="200"
          height="3"
        />
        <Component {...pageProps} />
      </>
    )
  }
}

export default MyApp

Web Thoughts

The web prop in the navigate function and Link component can help provide cleaner urls (user/mike instead of user?id=mike) on web.

Also, navigation patterns on mobile can be different than web, and this field can help you account for those situations.

For instance, imagine you have a tab navigator. Say the first tab has a nested stack navigator with an inbox screen and a chat room screen. If you navigate from a notifications tab to this tab, and a chat room screen was already open, you probably want that chat room to stay open on mobile. Only if you press the tab button a second time should it pop back to the inbox screen.

This may not be the case on web. Web navigation patterns on web may lead you to want to open the inbox directly, instead of the open chat screen. This example could look something like this:

navigate({
  routeName: 'inboxStack',
  web: {
    path: 'inbox',
  },
})

I've also considered letting the web field take a dynamic parameter like this chat/:roomId:

// goes to `yourdomain.com/chat/chris` and still passes `chris` as a `roomId` param
const navigateCleanLink = () =>
  navigate({
    routeName: 'chat',
    params: { roomId: 'chris' },
    web: { dynamic: `chat/[roomId]` },
  })

// goes to yourdomain.com/chat?roomId=chris
const onPress = () =>
  navigate({
    routeName: 'chat',
    params: { roomId: 'chris' },
  })

But that's not added. For now, the same is achieved by doing this:

const roomId = 'chris'

const navigateToChatRoom = () =>
  navigate({
    routeName: 'chat',
    params: { roomId },
    web: { path: `chat/${roomId}` },
  })

This would open the pages/chat/[roomId].js file, with roomId as a param.

More Repositories

1

moti

🐼 The React Native (+ Web) animation library, powered by Reanimated 3.
TypeScript
3,956
star
2

solito

🧍‍♂️ React Native + Next.js, unified.
TypeScript
3,426
star
3

dripsy

🍷 Responsive, unstyled UI primitives for React Native + Web.
TypeScript
1,918
star
4

zeego

Menus for React (Native) done right.
TypeScript
1,520
star
5

burnt

Crunchy toasts for React Native. 🍞
Java
1,140
star
6

swr-firestore

Implement Vercel's useSWR for querying Firestore in React/React Native/Expo apps. 👩‍🚒🔥
TypeScript
763
star
7

react-navigation-heavy-screen

⚡️Optimize heavy screens to prevent lags during React Navigation transitions.
TypeScript
320
star
8

react-native-anchors

🦅 Anchor links and scroll-to utilities for React Native (+ Web)
JavaScript
299
star
9

swr-react-native

React Native/React Navigation compatibility for Vercel's useSWR hook. 🐮
TypeScript
285
star
10

galeria

The React (Native) Image Viewer. 📷
TypeScript
145
star
11

expo-next-monorepo

💸 Expo + Next.js monorepo starter.
JavaScript
114
star
12

expo-spotify-party

🎹 Listen to Spotify with your friends in real-time. (React Native Expo + Next.js)
TypeScript
88
star
13

nextjs-conf-22-example

Example repository for the 2022 Next.js Conf about React Native.
TypeScript
67
star
14

react-native-iconic

🎨 Iconic icons for React Native (+Web)
TypeScript
65
star
15

react-native-heroicons

Hero Icons for React Native. 🫡
TypeScript
64
star
16

zero-to-10m

Resources from my talk at Next.js Conf: "Zero to $10 Million with React Native + Next.js"
57
star
17

expo-firestore-offline-persistence

❄️ Enable Firestore offline persistence in Expo apps without detaching.
JavaScript
51
star
18

tamagui-intellisense

VSCode Plugin for Tamagui
JavaScript
41
star
19

sf-symbols-typescript

TypeScript types for iOS SF Symbols 🍏
TypeScript
36
star
20

doorman

🚪Doorman is a full-stack phone authentication solution for Expo + Firebase auth.
TypeScript
29
star
21

reanimated-tree-shaking

A Next.js playground to test Reanimated bundle size.
TypeScript
24
star
22

sticky

Implement position: sticky in React Native
TypeScript
15
star
23

ts-monorepo-autoimport-guide

A guide for making VSCode's autoimport work in a monorepo.
12
star
24

expo-gatsby-navigation

🤵Make Gatsby and React Navigation play nicely together with an Expo/React Native Web app.
TypeScript
12
star
25

tgui

TypeScript
11
star
26

react-native-2030

The implementation of my App.js Conf talk, "React Native 2030"
11
star
27

fuego

🔥Firebase Firestore hooks & components for React Native/React.
TypeScript
10
star
28

shared-animations

🕺A global state manager for React Native animations.
JavaScript
10
star
29

expo-electron-copy-magic

Copy clip sucks, so this is a better one made with Expo + Electron.
TypeScript
10
star
30

lint-expo

🐠 A mini script that configures prettier and eslint for React/Expo projects.
JavaScript
9
star
31

tamagui-starter

Starter project for issue reproductions on CodeSandbox
TypeScript
8
star
32

doorman-examples

🥳A repository of example apps for Doorman (Expo + Firebase phone auth)
TypeScript
8
star
33

solito-test

TypeScript
6
star
34

react-doorman

React Core library for Doorman's Firebase Auth, without the RN dependencies. 👾Meant for use in Gatsby/other React apps.
TypeScript
4
star
35

expo-eas-semantic-release

git push → automated Expo EAS builds, over-the-air updates, & prerelease versions.
JavaScript
4
star
36

lexical-ios-react-native

Just an experiment with Expo Modules
Swift
4
star
37

expo-starter-project

eslint, src folder, etc
TypeScript
3
star
38

panda

🐼 Design websites in seconds. Export clean code. (PRE-ALPHA)
JavaScript
3
star
39

expo-navigation-core

Package with reusable expo web navigation hooks / components.
TypeScript
2
star
40

lint-react-native-app

🤓 Simple command to add ESLint and Prettier to a react-native project.
TypeScript
2
star
41

thet

TypeScript
2
star
42

animateMePlz

⚡️ A lightweight jQuery extension for scrolling animations.
HTML
2
star
43

expo-router-bug

TypeScript
1
star
44

plaid-bug-repro

Repro for https://github.com/plaid/react-native-plaid-link-sdk/issues/436
Objective-C
1
star
45

nandorojo

1
star
46

expo-next-issue

⚡️React-native-paper issue with expo / next.
JavaScript
1
star
47

react-native-bootstrap

React-bootstrap, remade for React Native (& React Native For Web).
TypeScript
1
star
48

reanimated-next13-issue

Reanimated isn't working with Next.js 13.
JavaScript
1
star
49

react-native-beacons-example

Repo for react-native-beacons
TypeScript
1
star
50

react-spring-next-issue

Reproducible repo for this issue: https://github.com/react-spring/react-spring/issues/627#issuecomment-541235141
TypeScript
1
star
51

next-style-issue

Issue: prop did not match, server: x, client: y for issue: https://github.com/vercel/next.js/discussions/14469
TypeScript
1
star
52

writing-javascript-actions

1
star
53

eslint-config-nando

Stress-free react native linting. 🏋🏻‍♂️
JavaScript
1
star
54

Penn-Requirement-Counter

🤓 Find courses that count for one or more requirements at UPenn.
JavaScript
1
star
55

expo-web-img-picker-bug

SDK 41 image picker not working on web.
JavaScript
1
star
56

expo-next-webpack5-issue

Reproduction of Webpack 5 not working with Expo in a Next.js app
JavaScript
1
star