# The Complete Guide to Phantom Deeplinks Source: https://phantom.com/learn/blog/the-complete-guide-to-phantom-deeplinks Introduction Solana’s fast and cheap transactions make it the perfect testing ground for novel mobile applications. Until recently, however, there was no way for native mobile apps to interact with existing Solana wallets. In order to connect to users, Solana dapps had to be web-based. In a mobile context, these web dapps were most often viewed within a wallet’s in-app browser. All of this changed in Phantom v22.04.01 with the release of Phantom deeplinks. With deeplinks, iOS and Android apps can now interact directly with the Phantom mobile app to connect, sign, and send transactions. In this tutorial, we’ll walk through how you can integrate Phantom deeplinks into your Solana mobile application. What are deeplinks? If you've ever created a link using an HTML tag, you're probably familiar with the mailto: scheme. Unlike a normal url that directs users straight to a website (i.e. href=""), the mailto: scheme tells the operating system that it should instead open the email application. Once open, your email app can then parse the url to present a rich user experience. For example, the url href="mailto:[email protected]?subject=Deeplinking Tutorial Feedback&body=Hi Brian," will compose an email to me with both a subject and body already pre-configured. These url schemes are often referred to as deeplinks. Just like using the mailto: scheme, other applications can also link to each other by using their own custom url schemes. When developing a mobile application, we can specify our own protocol handler (such as: discord:// or phantom://) and create deeplinks that tell the operating system to open our native app. We can even take it a step further: using a concept known as universal links, we can create deeplinks based on regular https:// urls for a domain that we control. This comes with the added benefits of being more secure, as well as a better user experience in cases where users have yet to install our app. Phantom supports both universal links ( https://phantom.app/ul/ ) as well as a custom protocol handler (phantom:// ). For the remainder of this tutorial, we'll focus on Phantom's universal links and refer to these collectively as deeplinks. What we're building To showcase the power of Phantom deeplinks, we'll be creating a mobile app that can interact with a program (aka smart contract) on Solana. Specifically, we’re going to be building a mobile-version of the movie reviews dapp from module 1 of the Solana Development Course. For the purposes of this tutorial, we won’t be writing any Solana programs ourselves. The Solana Development Course already does a great job of teaching that! Instead, we’ll be writing a mobile app that interacts with the course’s program which already exists on devnet. Once complete, our app will let users sign in and send reviews using Phantom. All in all, it'll look a little something like this: We'll be building this app with React Native and Expo CLI. If you're new to mobile app development, Expo makes it easy to get started within minutes. The majority of code we’ll be writing will be indistinguishable from web-based React and TypeScript. An added benefit of using Expo with React Native is that our app will be ready to use across both iOS and Android devices. Overview This tutorial covers 3 main sections: Creating our mobile app using React Native and Expo CLI Connecting our app to Phantom Signing and sending transactions via Phantom Creating our mobile app using React Native and Expo CLI Prerequisites Before we can build anything, we first need to install the necessary command-line tools. Chief amongst these are Node.js and Expo CLI. If you don’t have Node yet, I recommend installing it via Node Version Manager (NVM). Before proceeding, you should be able to run the following commands in your terminal. I developed mine with the following versions: node -v # output: v16.15.0 expo-cli --version # output: 5.5.1 In addition to our laptop’s development environment, we’ll also want a phone to test our React Native app. One of the many benefits of Expo is that it makes it easy to test your app on a physical device throughout the development process. Before proceeding, make sure to install both the Expo and Phantom mobile apps from your phone’s official app store. Scaffolding our React Native app With the necessary tools installed, we can get started with our app. To recap: we’re going to be building a mobile app for the movie reviews program in the Solana Development Course. Instead of rehashing the lessons from that course, I’ve added the necessary components to a starter repo located on our GitHub. To get started, simply fork the repo and clone it to your local developer environment: git clone -b starter https://github.com/phantom-labs/deep-links-movie-tutorial.git Then, change into the project directory and install the necessary dependencies: cd deep-links-movie-tutorial yarn Once you’ve installed the dependencies, you can fire up the app with: yarn start From here, your terminal should present you with a large QR code. Go ahead and grab your phone, open the camera app, and scan the QR code that is present on your laptop’s terminal. If you’re using an Android phone, you may need to scan it from within the Expo mobile app. If all goes well, your code should begin bundling and your phone will open the mobile app from the video above. Go ahead and try scrolling and searching within the app. If you’ve already completed the Solana Development Course’s module on Deserializing Custom Account Data, then this should look pretty familiar. Here, our app is fetching reviews from the course’s program and then paginating and filtering the results based on how far you scroll or what you search. If you take a look at our starter repo, you’ll see that the MovieList.tsx and MovieCoordinator.ts components from the course have already been integrated into our React Native app under the components directory. For the purposes of this tutorial, we won’t be going into detail over how these are working under the hood. If you’re interested in learning more here, be sure to check out the Solana Development Course! After playing around with our app, we can see that we’re already reading and displaying movies reviews that are stored on Solana. How would we go about adding new reviews? If you were to tap on the “Connect Phantom” button, you’ll see that nothing happens. Let’s see what’s going on by opening up the App.tsx file at the root of directory: import "react-native-get-random-values"; import "react-native-url-polyfill/auto"; import { Buffer } from "buffer"; global.Buffer = global.Buffer || Buffer; import { StatusBar } from "expo-status-bar"; import React, { useRef, useState } from "react"; import { ActivityIndicator, StyleSheet, Text, View } from "react-native"; import { clusterApiUrl, Connection, PublicKey, Transaction, } from "@solana/web3.js"; import MovieList from "./components/MovieList"; import Button from "./components/Button"; import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"; import ActionSheet from "react-native-actions-sheet"; import AddReviewSheet from "./components/AddReviewSheet"; import Toast from "react-native-toast-message"; import { toastConfig } from "./components/ToastConfig"; import { COLORS } from "./constants"; import * as Linking from "expo-linking"; const connection = new Connection(clusterApiUrl("devnet")); export default function App() { const [phantomWalletPublicKey, setPhantomWalletPublicKey] = useState(null); const [submitting, setSubmitting] = useState(false); const actionSheetRef = useRef(null); // Initiate a new connection to Phantom const connect = async () => {}; // Initiate a disconnect from Phantom const disconnect = async () => {}; // Initiate a new transaction via Phantom. We call this in `components/AddReviewSheet.tsx` to send our movie review to the Solana network const signAndSendTransaction = async (transaction: Transaction) => {}; // Open the 'Add a Review' sheet defined in `components/AddReviewSheet.tsx` const openAddReviewSheet = () => { actionSheetRef.current?.show(); }; return ( {phantomWalletPublicKey ? ( <> {`Connected to: ${phantomWalletPublicKey.toString()}`}