Linking works in both directions. Outward, Linking.openURL(url) hands a URL to the system, which opens a browser for https:, the dialer for tel:, the mail app for mailto: and maps for geo:; openSettings() opens your app's settings page, and on Android sendIntent(action, extras) fires any intent. Inward, a deep link opens your app at a particular screen. It needs a URL scheme, which Expo 6,418 declares from "scheme": "booknest" in app.json, and your code reads the URL with Linking.getInitialURL() on a cold start and the url event while the app runs.
const targets = ['https://example.com', 'mailto:orders@example.com', 'tel:+15550100',
'geo:0,0?q=bookshop', 'booknest://book/2', 'spotify:album:1'];
// ...inside LinkingDemo:
useEffect(() => {
Linking.getInitialURL().then((url) => console.log('initial URL:', url)); // cold start
const sub = Linking.addEventListener('url', ({ url }) => console.log('url event:', url));
(async () => {
for (const url of targets) console.log(url.padEnd(28), await Linking.canOpenURL(url));
})();
return () => sub.remove();
}, []);The app was opened from the launcher, and then sent a link with adb shell am start -a android.intent.action.VIEW -d booknest://book/2 com.anonymous.booknest:
LOG initial URL: null LOG https://example.com true LOG mailto:orders@example.com false LOG tel:+15550100 false LOG geo:0,0?q=bookshop false LOG booknest://book/2 true LOG spotify:album:1 false LOG url event: booknest://book/2
mailto: and tel: came back false although Gmail 1 and the dialer are installed, yet the Email the shop button's openURL('mailto:...') opened Gmail. Since Android 11, an app sees other apps only through the <queries> in its manifest, and the Expo template declares only https. Declare the schemes you ask about, or skip canOpenURL() and catch the rejection from openURL().