A set of hooks/components that wrap the react-navigation
API that you're used to, and make it work with Gatsby's built-in routing.
This library helps me use the Expo + Gatsby integration without stressing about navigation.
react-navigation
v5 is not yet supported, but I'm working on it now :)
Want to use next.js
with expo instead? Check out expo-next-react-navigation.
👾 Github Repo | 💻 Website
yarn add expo-gatsby-navigation
For react-navigation v5
:
yarn add expo-gatsby-navigation@v5
Install gatsby with expo:
Install expo: npm install -g expo-cli
Init: expo init woohoo
cd woohoo
Install gatsby: yarn add gatsby gatsby-plugin-react-native-web
Create a gatsby-config.js and use the plugin - touch gatsby-config.js
gatsby-config.js
module.exports = {
plugins: [
`gatsby-plugin-react-native-web`,
/* ... */
],
}
/.cache
and /public
to your .gitignore
fileAll done! Run yarn gatsby develop
& open http://localhost:8000 👻
I recommend becoming familiar gatsby
's architecture with expo
. Follow the docs on the Expo docs or see this article by Evan Bacon if you're curious.
Replace the following instances in your code after installation and setup:
useNavigation
👉 useRouting
-import { useNavigation } from 'react-navigation-hooks'
+import { useRouting } from 'expo-gatsby-navigation'
useLayoutEffect
-import { useLayoutEffect } from 'react-navigation-hooks'
+import { useLayoutEffect } from 'expo-gatsby-navigation'
<TouchableOpacity />
👉 <Link />
-import { TouchableOpacity } from 'react-native'
+import { Link } from 'expo-gatsby-navigation'
-<TouchableOpacity onPress={() => navigate({ routeName: 'chat' })}>
- <Text>Go</Text>
- </TouchableOpacity>
+<Link routeName="chat" params={{ roomId: 'hey!' }}>
+ Go
+</Link>
All set ⚡️
useRouting
React hook that wraps useNavigation
(from react-navigation) hook and makes it play nicely with Gatsby's built-in routing.
It follows the same API as useNavigation
.
import { useRouting } from 'expo-gatsby-navigation`
export default function Screen({ location }) {
const { navigation, push, getParam, goBack } = useRouting()
return <CoolComponent />
}
navigate
Only argument is a dictionary with these values. Unlike react-navigation
, this doesn't currently support a string as argument.
routeName
: string, requiredparams
: optional dictionaryweb
: Optional dictionary with added values for web, following the API from gatsby
's navigate
function.
to
: (optional string) Fulfills the same value as to
from gatsby
Link component, 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 src/pages/cars.js
as the screen.true
, replaces the current URL in history.Example: Navigate to a user
export default function Home() {
const { navigate } = useRouting()
// goes to yourdomain.com/user
// and sends the param to the screen component
// see getParam() docs for more
const onPress = () =>
navigate({
routeName: 'user',
params: { id: 'chris' },
})
// 👇or this👇
// goes to `yourdomain.com/profile`
const navigateCleanLink = () =>
navigate({
routeName: 'user',
params: { id: 'chris' },
web: { to: `/profile` },
})
// 👇or this👇
// 'profile' path overrides 'user' on web, so it uses the src/pages/profile.js file
// even though it navigates to yourdomain.com/profile
// this will also replace the current screen in the history
const navigateCleanLinkWithParam = () =>
navigate({
routeName: 'user',
params: { id: 'chris' }, // accessed with getParam and the location prop in the screen file
web: { replace: true, to: '/profile' },
})
}
For more thoughts on how and when you should use the web
field, see Web Thoughts.
getParam
Same API as getParam
from react-navigation, with an important difference.
Important: The getParam
function with gatsby is slightly less simple than the one offered by expo-next-react-navigation
. You have to provide a fallback value as the second argument. This is because gatsby doesn't expose a hook to access the location.
Example: src/pages/index.js
export default function Screen({ location }) {
const { getParam } = useRouting()
// when on web, it will return the value for location.state.userId
// web on mobile, it will use react-navigation
const userId = getParam('userId', location.state.userId)
}
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 below.
import { useFocusEffect } from 'expo-gatsby-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 src/pages/room.js
file for Gatsby.
Optionally accepts a gatsbyLinkProps
prop dictionary and touchableOpacityProps
dictionary as well.
export default function Button() {
return (
<Link
routeName="chat"
params={{ roomId: '12' }}
web={{
to: '/room',
replace: false,
}}
>
Chat in room 12
</Link>
)
}
Required props:
routeName
: string, see useRouting().navigate
docs.children
: stringOptional props
web
: dictionary, see useRouting().navigate
docs.
touchableOpacityProps
: extends React Native's TouchableOpacity
props.
gatsbyLinkProps
: extends gatsby
's Link props.
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: {
to: '/inbox',
},
})