IOS Universal Links with Expo, React Navigation and React Native
July 23, 2025TLDR
After following Expo's guide on how to use Universal Links found here:
and Configuring Deep Linking for React Navigation with the following guides:
I had deep link working but did not have Universal Links working. I could link into my application with some arbitrary scheme such as:
1pokemon://pokemon/1
Which I had defined by setting it in the app.json file in my expo project. Read more about URL schemes here: https://docs.expo.dev/versions/latest/config/app/#scheme.
But if I would use a URL like: https://pokemon-web-chi.vercel.app/pokemon/1 . This link would not open in my application like I intended but would launch inside my web browser. even though I followed the Expo guide strictly with how to configure my website and application to be associated.
Expo in their guide suggest you to add a list of associated domains leading with "applinks" (so apple know they are universal links) inside the ios object of your app.json like the following:
1"ios": {
2//....
3 "bundleIdentifier": "com.cjoshmartin.pokemon",
4 "associatedDomains": [
5 "applinks:pokemon-web-chi.vercel.app"
6 ],
7 //...
8 }
In addition for me to get this to work, you also need to add an entitlement for associated domains that you want to use, which should look like the following:
1"ios": {
2//....
3 "bundleIdentifier": "com.cjoshmartin.pokemon",
4 "associatedDomains": [
5 "applinks:pokemon-web-chi.vercel.app"
6 ],
7 "entitlements": {
8 "com.apple.developer.associated-domains": [
9 "applinks:pokemon-web-chi.vercel.app"
10 ]
11 },
12 //...
13 }
As a note, for the client I am doing this work for they have multiple environments for their deployments and I have tried prefixing a wildcard (*) selector for their multiple subdomains like "dev", "staging", or "web". For me that doesn't seem to get picked up and it is better just to list out all the subdomains in the entitlements array and the associatedDomains array.
After adding the entitlements for associated domains it seemed to work fine for me! I was able to click on a URL and it would jump into my test application with no issue.
Which is great! And if you were stuck like I was probably all you need for now and you can find the code at the bottom of this post... if you are still confused and want to get a little deeper please keep reading this article.
Deep Diving into it
Deep Links
As defined by wikipedia, they are links to a specific location within in a mobile application usually defined by a unique uniform resource identifier (URI) created by the application itself. Such as these ones:
- fb:// is the iOS URI to launch Facebook's mobile app
- YouTube:// is the iOS URI to launch YouTube's mobile app
You can linking to a specific page inside the mobile application or pass data as you open the app like the following:
1fb://profile/33138223345
This will open the Facebook app to a profile that has the id of "33138223345".
The downside of this self defined URI structure is that it doesn't go with the standard URL HTTP structure we are use to using because of the web making these deep links less flexible then normal urls.
Universal Links
Universal Links are a protocol that build on top of the URIs of Deep Linkings but allow app developers of IOS apps to use the "HTTPS" Protocol that they are already using for their web application to link into their IOS using the same path scheme. For example this deep linking URI:
1fb://profile/33138223345
Would look like the following (as a Universal Link):
1https://facebook.com/profile/33138223345
This is done by creating a two way association between your website and your IOS application.
Setting up Universal Links
- In the deep linking guide provided by React Navigation, the first step is to define the deep linking URI in your "app.json" file like the following:
1{
2 "expo": {
3 // ...
4 "scheme": "pokemon",
5 // ...
6 }
7}
View this permlink to look at the full example: https://github.com/cjoshmartin/pokemon-mobile/blob/951183d09d9b34d5a61697aceb7ecf5a48430789/app.json#L10
Also you will need to install expo-linking to connect React Navigation to the linking scheme you have define in your "app.json":
1npx expo install expo-linking
2. In the App.tsx we need to update our linking object to have the permutations of our deep link URI and our web URL
1const prefix = Linking.createURL('/');
2
3export function App() {
4 const colorScheme = useColorScheme();
5
6 const theme = colorScheme === 'dark' ? DarkTheme : DefaultTheme
7
8 return (
9 <Navigation
10 theme={theme}
11 linking={{
12 enabled: 'auto',
13 prefixes: [
14 // Change the scheme to match your app's scheme defined in app.json
15 prefix,
16 'pokemon://',
17 'http://pokemon-web-chi.vercel.app',
18 'https://pokemon-web-chi.vercel.app',
19 'https://pokemon-web-chi.vercel.app/pokemon/*',
20 'http://pokemon-web-chi.vercel.app/pokemon/*',
21 ],
22 }}
23 onReady={() => {
24 SplashScreen.hideAsync();
25 }}
26 />
27 );
28}
Here I try to add all the forms of the urls I am wanting to pick up. I am not sure if that is necessary and maybe there is some trial and error to be done here but when I don't add all the links and paths it does not seem to work for me.
you can view the full file here: https://github.com/cjoshmartin/pokemon-mobile/blob/main/src/App.tsx
At this point we can link into our application using the deep link URI of "pokemon://", for example we can link into the first pokemon with the follow URI scheme:
1pokemon://pokemon/1
3. Set your bundle Identifier in your "app.json" and publish to Apple Connect
1{
2 "expo": {
3 "ios": {
4 "bundleIdentifier": "com.cjoshmartin.pokemon"
5 }
6 }
7}
Once again you can read the full app.json here: https://github.com/cjoshmartin/pokemon-mobile/blob/951183d09d9b34d5a61697aceb7ecf5a48430789/app.json#L13
and if you run the following command it will publish your application to apple connect and give you a file called the "apple-app-site-association" which we will use to start associating your domain with your application:
1npx setup-safari
the example of the "apple-app-site-association" that it will give you will look like this:
1{
2 "applinks": {
3 "details": [
4 {
5 "appIDs": [
6 "HJXLN2SCZS.com.cjoshmartin.pokemon"
7 ],
8 "components": [
9 {
10 "/": "*",
11 "comment": "Matches all routes"
12 }
13 ]
14 }
15 ]
16 },
17 "activitycontinuation": {
18 "apps": [
19 "HJXLN2SCZS.com.cjoshmartin.pokemon"
20 ]
21 },
22 "webcredentials": {
23 "apps": [
24 "HJXLN2SCZS.com.cjoshmartin.pokemon"
25 ]
26 }
27}
One change I made was to change the components object to a paths object which just shows an array of url paths you want your app to handle:
1{
2 "applinks": {
3 "details": [
4 {
5 "appID": "HJXLN2SCZS.com.cjoshmartin.pokemon",
6 "path": ["/pokemon/*"]
7 }
8 ]
9 }
10}
View the Permlink here: https://github.com/cjoshmartin/pokemon-web/blob/main/public/.well-known/apple-app-site-association
Next we need to add this JSON file to a place where it could be accessible via a path like "https://[website]/.well-known/apple-app-site-association"
For me, I am using Next.JS so that I need to create a folder of ".well-known" in the public folder in my project with a file of "apple-app-site-association". This will serve the file at the correct path.
you can view the live file here: http://pokemon-web-chi.vercel.app/.well-known/apple-app-site-association
4. Lastly we need to configure the application to associate the domain with itself by once again updating the app.json with associate domains and entitlements like the following:
1{
2 "expo": {
3 "ios": {
4 "associatedDomains": ["applinks:pokemon-web-chi.vercel.app"],
5 "entitlements": {
6 "com.apple.developer.associated-domains": [
7 "applinks:pokemon-web-chi.vercel.app"
8 ]
9 }
10 }
11 }
12}
Make sure to list out all permutations of your domains in these arrays such as dev, prod, stage or other subdomains that you want to open links in your application.
After this we just need to build the application and submit it, to register it with the associated domains we want to use. Using the following command can do that for us:
1eas build --platform=ios --profile=production --non-interactive && eas submit -p ios --latest
We can download the latest build via testflight and test it out:
And Boom! It works great!
you can find the code here:

Josh Martin
React.JS Developer and Maker
contact@cjoshmartin.com
Chicago, IL
Hello, I am Josh, I am a full-stack developer specializing in developing for Web and Mobile in React, React Native, Node.js, and Django. I used to work at Amazon in the advertising sales Performance department as a Frontend Engineer...
I have a degree from Purdue University in Computer Engineering with the use of my degree and passions I can offer support more than just Web and Mobile development. I can assist with any need related to hardware integration with internet-enabled devices or design needs in CAD and manufacturing.