Deep linking
Thanks to Expo Router's file-system based routing, we get deep linking out of the box. But what does that actually mean?
Because of the constraints we have in defining screens in the app folder, each screen has an exact unambiguous url. This means that Expo Router can (and does) support deep linking out of the box.
So what is deep linking? It is opening a specific page in your app from outside of the app. Apps register to listen to a specific scheme - recall how we set up a scheme in our app.json back when we installed Expo Router? That will be the keyword our app listens to. The scheme isn't guaranteed to be unique, and when multiple apps register to the same scheme, on Android you'll get prompted to choose which app to open, and on iOS the OS just opens one.
For some more advanced reading on deep links, check out Universal and App Links. With this method, you upload a specific file on your website, and add it as an associated domain to your app. This sets up a more robust deep linking flow.
The scheme looks like this: plantly://
. It looks similar to http://
, mailto://
or tel://
? It's because it using a similar system for hooking into the native app with an intent.
In our app, plantly://
will open the home page, but we can specify which screen to link to based on the url! So e.g. we can link to a plants details page with plantly://plants/1
.
Test a deep link in a Simulator / Emulator
To open a deep link in your iOS simulator, run:
npx uri-scheme open plantly://plants/1 --ios
For the Android emulator, run:
npx uri-scheme open plantly://plants/1 --android
Read more in the docs.
This is sure to come up once you get further in your development journey.
On iOS you can type the url into Safari and it will prompt to open the deep link.
The same won't work on Android, but an easy way to get around this is to find a website that supports live editing HTML (like this), add your link to an a
tag:
<a href="plantly://plants/1">Click me</a>
and click it on the phone.
Initial route name
You might have noticed something weird going on if we deep link to out plant details page from a completely closed app: there is no way to back to the home page!
We'll need to configure this in the layout file using unstable settings ("unstable" because it does not work with async routes, fine for us to use):
Update: app/(tabs)/(home)/_layout.tsx
@@ -3,6 +3,10 @@ import { Link, Stack } from "expo-router";
import { Pressable } from "react-native";
import { theme } from "../../../theme";
+export const unstable_settings = {
+ initialRouteName: "index",
+};
+
export default function Layout() {
return (
<Stack>
Now when you deep link from a completely closed app, it'll correctly show a back button.
Passing parameters in your deep link
You can also pass in parameters to your deep link. Really you can add anything there, but the convention is to use url syntax.
Add a console.log(params)
to the plant details page an open the app with
npx uri-scheme open plantly://plants/1\?query=something --ios
The url param shows up! Neat!
Let's pass in an action so that if we receive ?action=water
we'll automatically water the plant.
npx uri-scheme open plantly://plants/1\?action=water --ios
Now add add a useEffect
that waters the plans when this query param is passed in:
Update: app/(tabs)/(home)/plants/[plantId].tsx
@@ -19,6 +19,14 @@ export default function PlantDetails() {
);
const navigation = useNavigation();
+ useEffect(() => {
+ if (params.action === "water") {
+ if (typeof plantId === "string") {
+ waterPlant(plantId);
+ }
+ }
+ }, []);
+
useEffect(() => {
navigation.setOptions({
title: plant?.name,
Checkpoint
Though we didn't use any in the app, the same action could be triggered in-app with this action button: yarn
<Link href={{ pathname: "plants/1", params: { action: "water" } }}>
Water me
</Link>