Skip to main content

Requirements

  • A React Native project using the latest version
  • iOS and Android platform support (Web is not supported)

Prerequisites

First, ensure you have the necessary development tools:
brew update
brew install watchman

Installation

Core Dependencies

Install the development client and core dependencies:
npx expo install expo-dev-client
npx expo install expo-apple-authentication expo-application expo-crypto expo-linking expo-secure-store expo-web-browser react-native-passkeys react-native-webview @privy-io/expo-native-extensions @privy-io/expo

Required Polyfills

Install the necessary polyfills:
npm i fast-text-encoding react-native-get-random-values
If your app uses the Expo bare workflow (“React Native without Expo”), also run:
npx pod-install

Build Configuration

Generate the native code to enable native module configuration:
npx expo prebuild
The expo prebuild command builds the workspace so you can edit the app delegate and other native configurations.

Configure Polyfills

Create an entrypoint.js file and update your package.json:
entrypoint.js
// Import required polyfills first
import 'fast-text-encoding';
import 'react-native-get-random-values';
// Then import the expo router
import 'expo-router/entry';
package.json
{
  "name": "<your app name>",
  "main": "entrypoint.js"
}
If you’re using the @solana/web3.js package, install the buffer dependency:
npm i buffer
And add this code after importing react-native-get-random-values:
import 'react-native-get-random-values';
import {Buffer} from 'buffer';
global.Buffer = Buffer;
This guide ensures that your application satisfies the following requirements for integrating:

Enabling Package Exports

React Native 0.79, and Expo 53, have enabled package exports by default.Some popular packages present incompatibilities with this change, and the community is working to get these fixed at source. In the meantime, we present a fix below by disabling package exports for the incompatibilities we have found.
Update your metro.config.js like so:
//...other config logic

// Enable package exports for select libraries
...
const resolveRequestWithPackageExports = (context, moduleName, platform) => {
  // Package exports in `isows` (a `viem`) dependency are incompatible, so they need to be disabled
  if (moduleName === "isows") {
    const ctx = {
      ...context,
      unstable_enablePackageExports: false,
    };
    return ctx.resolveRequest(ctx, moduleName, platform);
  }

  // Package exports in `zustand@4` are incompatible, so they need to be disabled
  if (moduleName.startsWith("zustand")) {
    const ctx = {
      ...context,
      unstable_enablePackageExports: false,
    };
    return ctx.resolveRequest(ctx, moduleName, platform);
  }

  // Package exports in `jose` are incompatible, so the browser version is used
  if (moduleName === "jose") {
    const ctx = {
      ...context,
      unstable_conditionNames: ["browser"],
    };
    return ctx.resolveRequest(ctx, moduleName, platform);
  }

  // The following block is only needed if you are
  // running React Native 0.78 *or older*.
  if (moduleName.startsWith('@privy-io/')) {
    const ctx = {
      ...context,
      unstable_enablePackageExports: true,
    };
    return ctx.resolveRequest(ctx, moduleName, platform);
  }

  return context.resolveRequest(context, moduleName, platform);
};

config.resolver.resolveRequest = resolveRequestWithPackageExports;

...
module.exports = config;

Typescript’s Module Resolution

Also configure your tsconfig.json like so:
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    // Allows us to use conditional/deep imports on published packages
    "moduleResolution": "Bundler"
  }
}