Skip to main content

Introduction

This guide will help you use the Phantom redirect-based connect feature on mobile. Instead of deeplinking into the Phantom in-app browser, your users can enjoy a redirect-based connection, where they are automatically redirected between the app and their wallet to facilitate the connect, sign and additional method calls.

Why redirect events are necessary

Unlike browser-extension wallets (like MetaMask or Phantom browser extension), Phantom on mobile operates via deep links. When you request a signature:
  1. Your app opens Phantom via a deep link (phantom://)
  2. User approves or rejects in the Phantom app
  3. Phantom redirects back to your app
On mobile web browsers, this redirect opens a new tab, which means:
  • The original JavaScript execution context is lost
  • Any Promise awaiting the signature result never resolves
  • Your app loses track of the pending operation
This is fundamentally different from browser-extension wallets where everything happens in the same page context via injected providers. To handle this, you must use event listeners to capture the redirect results.

Setup

mobileExperience prop

Make sure you set mobileExperience to redirect on your DynamicContextProvider:
<DynamicContextProvider
  settings={{
    mobileExperience: 'redirect'
    // ... other settings
  }}
>
With just this code, your users will connect with the redirect-based approach. If you want to learn how to make method calls (like signAndSendTransaction) and access the result, read on.

Handling redirect events

When using redirect-based signing on mobile, Phantom opens via deep link and redirects back after the user approves or rejects. Because the original Promise context is lost, you must use event listeners to capture the results.
Use the usePhantomRedirectEvents hook to listen for redirect results:
import { usePhantomRedirectEvents } from '@dynamic-labs/sdk-react-core';

const MyComponent = () => {
  usePhantomRedirectEvents({
    onSignMessage: ({ signature, errorCode, errorMessage }) => {
      if (signature) {
        console.log('Message signed:', signature);
      } else {
        console.error('Sign failed:', errorCode, errorMessage);
      }
    },
    onSignAndSendTransaction: ({ signature, transaction, errorCode, errorMessage }) => {
      if (signature) {
        console.log('Transaction sent:', signature);
      } else {
        console.error('Transaction failed:', errorCode, errorMessage);
      }
    },
    onSignTransaction: ({ signature, transaction, errorCode, errorMessage }) => {
      // Handle signed transaction
    },
    onSignAllTransactions: ({ signature, transactions, errorCode, errorMessage }) => {
      // Handle signed transactions
    },
  });

  return <div>Your app content</div>;
};
Mount this hook in a component that renders when your app loads so it can capture the redirect result.

Available events

Event / CallbackPayloadDescription
signMessageResult / onSignMessage{ signature?, message?, requestId?, errorCode?, errorMessage? }Fired after signing a message.
signTransactionResult / onSignTransaction{ transaction?, requestId?, errorCode?, errorMessage? }Fired after signing a single transaction.
signAndSendTransactionResult / onSignAndSendTransaction{ signature?, transaction?, requestId?, errorCode?, errorMessage? }Fired after signing and broadcasting a transaction.
signAllTransactionsResult / onSignAllTransactions{ transactions?, requestId?, errorCode?, errorMessage? }Fired after signing multiple transactions.
All callbacks/events are optional. The events are persisted and re-emitted when the app regains focus, ensuring you always receive the result even if the app was backgrounded.