Expo Linking provides comprehensive deep linking functionality for React Native and Expo applications, enabling developers to create, parse, and handle universal deep links across iOS, Android, and web platforms. It offers a unified API that wraps React Native's native Linking module while adding enhanced parsing capabilities, URL validation, and cross-platform compatibility.
npm install expo-linkingimport * as Linking from "expo-linking";Named imports:
import {
openURL,
canOpenURL,
getInitialURL,
parseInitialURLAsync,
addEventListener,
createURL,
parse,
useLinkingURL,
type ParsedURL,
type CreateURLOptions,
type URLListener,
type EventType
} from "expo-linking";EmitterSubscription import (for event listeners):
import { EmitterSubscription } from "react-native";For CommonJS:
const Linking = require("expo-linking");
const { openURL, canOpenURL, createURL, parse } = require("expo-linking");import { createURL, parse, openURL, canOpenURL } from "expo-linking";
// Create a deep link URL
const url = createURL("profile/123", {
queryParams: { tab: "settings" }
});
// Result: "myapp://profile/123?tab=settings"
// Parse a URL
const parsed = parse(url);
console.log(parsed.path); // "profile/123"
console.log(parsed.queryParams); // { tab: "settings" }
// Open a URL
const canOpen = await canOpenURL("https://example.com");
if (canOpen) {
await openURL("https://example.com");
}import {
createURL,
parse,
parseInitialURLAsync,
useLinkingURL,
addEventListener,
type EventType
} from "expo-linking";
import { useEffect } from "react";
// App.tsx - Complete deep linking setup
function App() {
const url = useLinkingURL();
// Handle initial URL when app launches
useEffect(() => {
async function handleInitialURL() {
const initialUrl = await parseInitialURLAsync();
if (initialUrl.path) {
handleDeepLink(initialUrl);
}
}
handleInitialURL();
}, []);
// Handle URL changes while app is running
useEffect(() => {
if (url) {
const parsed = parse(url);
handleDeepLink(parsed);
}
}, [url]);
function handleDeepLink(parsed: { path: string | null; queryParams: any }) {
if (!parsed.path) return;
const [screen, ...params] = parsed.path.split('/');
switch (screen) {
case 'profile':
navigateToProfile(params[0], parsed.queryParams);
break;
case 'settings':
navigateToSettings(parsed.queryParams?.tab);
break;
default:
console.log('Unknown deep link:', parsed.path);
}
}
// Generate shareable links
function createShareableLink(screen: string, id?: string) {
return createURL(`${screen}${id ? `/${id}` : ''}`, {
queryParams: { utm_source: 'share' }
});
}
return <YourAppContent />;
}Expo Linking is built around several key components:
Essential URL operations including opening URLs, checking URL support, and retrieving the app's initial launch URL.
function openURL(url: string): Promise<true>;
function canOpenURL(url: string): Promise<boolean>;
function getInitialURL(): Promise<string | null>;
function getLinkingURL(): string | null;Create properly formatted deep links and parse URL components with support for Expo-specific URL patterns and cross-platform compatibility.
function createURL(path: string, options?: CreateURLOptions): string;
function parse(url: string): ParsedURL;
function parseInitialURLAsync(): Promise<ParsedURL>;
interface CreateURLOptions {
scheme?: string;
queryParams?: QueryParams;
isTripleSlashed?: boolean;
}
interface ParsedURL {
scheme: string | null;
hostname: string | null;
path: string | null;
queryParams: QueryParams | null;
}React hooks and event listeners for responding to URL changes and deep link events in your application.
function addEventListener(type: 'url', handler: URLListener): EmitterSubscription;
function useURL(): string | null; // deprecated
function useLinkingURL(): string | null;
type URLListener = (event: EventType) => void;
interface EventType {
url: string;
nativeEvent?: MessageEvent;
}Advanced scheme resolution and validation for Expo and React Native applications, handling development vs production environments.
function hasCustomScheme(): boolean;
function collectManifestSchemes(): string[];
function hasConstantsManifest(): boolean;
function resolveScheme(options: { scheme?: string; isSilent?: boolean }): string;type QueryParams = Record<string, undefined | string | string[]>;
interface SendIntentExtras {
key: string;
value: string | number | boolean;
}
type NativeURLListener = (nativeEvent: MessageEvent) => void;