Universal Links are a powerful feature on iOS that allows your app to open directly when a user taps on a link, rather than opening the link in a web browser. Before diving into Universal Links, it’s important to understand the foundation of deep linking. Deep links enable direct navigation to specific content within your app, enhancing the user experience. Check out our guide on Deep Link to get a solid grasp before setting up Universal Links.
This guide will walk you through the process of setting up Universal Links in a React Native app using Expo, as well as configuring the necessary web server settings.
Prerequisites
- A React Native app created with Expo.
- A custom domain for hosting your Universal Links configuration file.
- Firebase Hosting or any other web hosting service.
Step 1: Configure the Apple App Site Association (AASA) File
The AASA file is a JSON file that establishes a secure association between your domain and your app. This file must be hosted on your web server.
1.1 Create the AASA File
1. Create the .well-known folder: This folder should be placed in the root directory of your web server.
2. Create the apple-app-site-association
file: Inside the .well-known
folder, create a file named apple-app-site-association
(without a file extension).
3. Add the following content to the AASA file:
{
"applinks": {
"apps": [],
"details": [
{
"appId": "$TEAM_ID.com.yourcompany.yourapp",
"paths": [
"NOT /_/*",
"/*"
]
}
]
}
}
- $TEAM_ID: Your Apple Developer Team ID.
- com.yourcompany.yourapp: Your app's bundle identifier.
4. Verify the AASA file: Navigate to https://your-domain.com/.well-known/apple-app-site-association
to ensure the file is accessible.
5 (Possible Error): On some occasions, depending on your hosting provider, it may happen that the redirect is done incorrectly because the apple-app-site-association file doesn't have an extension.
If this happens, copy and paste this file with the extension .json apple-app-site-association.json into the .well-known folder, then add a redirect to your hosting provider. Something like this:
https://github.com/aws-amplify/amplify-hosting/issues/1983#issuecomment-881549611
Step 2: Configure Your React Native App
2.1 Install Required Packages
1. Install expo-linking
expo install expo-linking
2. Install react-navigation (if not already installed):
npm install @react-navigation/native
2.2 Configure Deep Linking in Your App
1. Update app.json:
Add the following configuration to your app.json file:
{
"expo": {
"scheme": "yourapp",
"ios": {
"bundleIdentifier": "com.yourcompany.yourapp",
"associatedDomains": ["applinks:your-domain.com"]
},
"android": {
"autoVerify": true,
"intentFilters": [
{
"action": "VIEW",
"data": [
{
"scheme": "https",
"host": "your-domain.com"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
}
}
}
- scheme: A custom URL scheme for your app.
- bundleIdentifier: Your app's bundle identifier.
- associatedDomains: The domain(s) associated with your Universal Links.
- autoVerify: is required for Android App Links to work correctly
- intentFilters: Configuration for setting an array of custom intent filters in Android manifest.
2. Configure Deep Linking in Your App:
In your main app file (e.g., App.js
), configure deep linking:
import { Linking } from 'expo';
import { NavigationContainer } from '@react-navigation/native';
const prefix = Linking.createURL('/');
export default function App() {
const linking = {
prefixes: [prefix, 'https://your-domain.com'],
config: {
screens: {
Home: 'home',
Profile: 'profile/:id',
// Add other screens here
},
},
};
return (
<NavigationContainer linking={linking}>
{/* Your app components */}
</NavigationContainer>
);
}
If you are using expo-router, follow the code below:
import { Stack } from 'expo-router';
import { LinkingOptions } from '@react-navigation/native';
const linking: LinkingOptions<ReactNavigation.RootParamList> = {
prefixes: ['https://your-domain.com', 'yourapp://'],
config: {
screens: {
// Automatically maps file-based routes to URLs
index: '/', // Maps to the root route
profile: 'profile/:id', // Maps to /profile/:id
settings: 'settings', // Maps to /settings
// Add other routes here
},
},
};
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="profile/[id]" />
<Stack.Screen name="settings" />
</Stack>
);
}
2.3 Handle Incoming Links
1. Add a listener for incoming links:
import { useEffect } from 'react';
import { Linking } from 'react-native';
const handleDeepLink = (event) => {
const { url } = event;
// Handle the URL, e.g., navigate to the appropriate screen
console.log('Incoming link:', url);
};
useEffect(() => {
Linking.addEventListener('url', handleDeepLink);
return () => {
Linking.removeEventListener('url', handleDeepLink);
};
}, []);
Step 3: Test Your Universal Links
1. Build and run your app:
expo build:ios
expo build:android
2. Test the Universal Link:
- Send a Universal Link (e.g., https://your-domain.com/home
) to your device.
- Tap the link and ensure it opens your app directly.
3. Important:
Apple’s content delivery network requests the apple-app-site-association file for your domain within 24 hours. Devices check for updates approximately once per week after app installation.
Conclusion
Following this guide, you should now have a fully functional Universal Link setup for your React Native app using Expo. This setup allows your app to handle deep links seamlessly, providing a better user experience. Remember to test your links thoroughly to ensure they work as expected across different devices and scenarios.
Do not forget to explore our in-depth guide on What is a Deep Link and How to Do It? and unlock the full potential of deep linking.
Happy coding! 🚀
Tags
Subscribe to
Our
Newsletter
Join 1,000+ people and recieve our weekly insights.

Success!
Thank your for subscribing to Buzzvel's
Newsletter, you will now
receive
amazing
tips
and insights weekly.