1

Install the SDK

Run the following command to install the SDK:
npm i @crossmint/client-sdk-react-native-ui
2

Add the Crossmint providers to your app

Add the necessary Crossmint providers to your app. This example uses Crossmint Auth but you can use any authentication provider of your choice.With the current setup, a wallet will be created automatically on login.See all supported chains here.
providers.tsx
import {
    CrossmintAuthProvider,
    CrossmintProvider,
    CrossmintWalletProvider,
} from "@crossmint/client-sdk-react-native-ui";

type ProvidersProps = {
    children: React.ReactNode;
};

export default function CrossmintProviders({ children }: ProvidersProps) {

    return (
        <CrossmintProvider apiKey={"<your-client-api-key>"}>
            <CrossmintAuthProvider>
                <CrossmintWalletProvider
                    createOnLogin={{
                        chain: "<your-chain>",
                        signer: {
                            type: "<signer-type>",
                        }
                    }}
                >
                    {children}
                </CrossmintWalletProvider>
            </CrossmintAuthProvider>
        </CrossmintProvider>
    );
}

Configuring the Wallet Provider

createOnLogin
object
If set creates a wallet on login using the specified configuration.
3

Allow users to login and logout and access their wallet

Crossmint Auth in React Native is headless. You will need to implement your own login and logout buttons.
index.tsx
import React, { useEffect, useState } from "react";
import {
    View,
    Text,
    TextInput,
    TouchableOpacity,
    ActivityIndicator,
    Alert,
} from "react-native";
import {
    useCrossmintAuth,
    useWallet,
} from "@crossmint/client-sdk-react-native-ui";
import * as Linking from "expo-linking";

export default function Index() {
    const {
        loginWithOAuth,
        createAuthSession,
        user,
        crossmintAuth,
        logout,
        status,
    } = useCrossmintAuth();
    const { wallet } = useWallet();

    // Login state
    const [email, setEmail] = useState("");
    const [emailId, setEmailId] = useState("");
    const [otpSent, setOtpSent] = useState(false);
    const [otp, setOtp] = useState("");
    const [isPending, setIsPending] = useState(false);

    // Handle OAuth callback
    useEffect(() => {
        const url = Linking.useURL();
        if (url != null) {
            createAuthSession(url);
        }
    }, [createAuthSession]);

    // Login functions
    const sendOtp = async () => {
        if (!email.trim()) {
            Alert.alert("Error", "Please enter a valid email address");
            return;
        }

        setIsPending(true);
        try {
            const res = await crossmintAuth?.sendEmailOtp(email);
            setEmailId(res.emailId);
            setOtpSent(true);
        } catch (error) {
            Alert.alert("Error", "Failed to send OTP. Please try again.");
        } finally {
            setIsPending(false);
        }
    };

    const verifyOtp = async () => {
        if (!otp.trim()) {
            Alert.alert("Error", "Please enter the OTP code");
            return;
        }

        setIsPending(true);
        try {
            const oneTimeSecret = await crossmintAuth?.confirmEmailOtp(
                email,
                emailId,
                otp
            );
            await createAuthSession(oneTimeSecret);
        } catch (error) {
            Alert.alert("Error", "Invalid OTP code. Please try again.");
        } finally {
            setIsPending(false);
        }
    };

    const handleLogout = async () => {
        try {
            logout();
            // Reset login state
            setEmail("");
            setEmailId("");
            setOtpSent(false);
            setOtp("");
        } catch (error) {
            Alert.alert("Error", "Failed to logout. Please try again.");
        }
    };

    // Loading state
    if (status === "initializing") {
        return (
            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
                <ActivityIndicator size="large" />
                <Text>Initializing...</Text>
            </View>
        );
    }

    // Login screen
    if (status === "logged-out") {
        return (
            <View style={{ flex: 1, padding: 20, justifyContent: "center" }}>
                <Text style={{ fontSize: 24, marginBottom: 20, textAlign: "center" }}>
                    Login
                </Text>

                <TextInput
                    style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
                    placeholder="Enter your email"
                    value={email}
                    onChangeText={setEmail}
                    editable={!otpSent}
                />

                {!otpSent ? (
                    <TouchableOpacity
                        style={{
                            backgroundColor: "#05b959",
                            padding: 15,
                            alignItems: "center",
                        }}
                        onPress={sendOtp}
                        disabled={isPending}
                    >
                        {isPending ? (
                            <ActivityIndicator color="#fff" size="small" />
                            ) : (
                            <Text style={{ color: "#fff" }}>Send OTP</Text>
                        )}
                    </TouchableOpacity>
                    ) : (
                    <>
                        <TextInput
                            style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
                            placeholder="Enter OTP code"
                            value={otp}
                            onChangeText={setOtp}
                        />
                        <TouchableOpacity
                            style={{
                                backgroundColor: "#05b959",
                                padding: 15,
                                alignItems: "center",
                                marginBottom: 10,
                            }}
                            onPress={verifyOtp}
                            disabled={isPending}
                        >
                            {isPending ? (
                                <ActivityIndicator color="#fff" size="small" />
                            ) : (
                                <Text style={{ color: "#fff" }}>Verify OTP</Text>
                            )}
                        </TouchableOpacity>
                        <TouchableOpacity
                            style={{
                                backgroundColor: "#ccc",
                                padding: 15,
                                alignItems: "center",
                            }}
                            onPress={() => setOtpSent(false)}
                            disabled={isPending}
                        >
                            <Text>Back</Text>
                        </TouchableOpacity>
                    </>
                )}

                <View style={{ marginVertical: 20 }}>
                    <Text style={{ textAlign: "center", marginBottom: 10 }}>OR</Text>
                    <TouchableOpacity
                        style={{
                            backgroundColor: "#4285f4",
                            padding: 15,
                            alignItems: "center",
                        }}
                        onPress={() => loginWithOAuth("google")}
                    >
                        <Text style={{ color: "#fff" }}>Sign in with Google</Text>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }

    // Main app screen (logged in)
    return (
        <View style={{ flex: 1, padding: 20 }}>
            <View
                style={{
                    flexDirection: "row",
                    justifyContent: "space-between",
                    marginBottom: 20,
                }}
            >
                <Text style={{ fontSize: 20 }}>Welcome!</Text>
                <TouchableOpacity onPress={handleLogout}>
                    <Text style={{ color: "red" }}>Logout</Text>
                </TouchableOpacity>
            </View>

            <View style={{ marginBottom: 20 }}>
                <Text style={{ fontSize: 16, marginBottom: 5 }}>User Info:</Text>
                <Text>Email: {user?.email}</Text>
                <Text>User ID: {user?.id}</Text>
            </View>

            {wallet && (
                <View style={{ marginBottom: 20 }}>
                    <Text style={{ fontSize: 16, marginBottom: 5 }}>Wallet Info:</Text>
                    <Text>Address: {wallet.address}</Text>
                </View>
            )}

            <View>
                <Text style={{ fontSize: 16, marginBottom: 10 }}>App Content:</Text>
                <Text>You are now logged in and can access your wallet!</Text>
            </View>
        </View>
    );
}

Launching in Production

For production, the steps are almost identical, but some changes are required:
  1. Create a developer account on the production console
  2. Create a production client API key on the API Keys page with the API scopes users.create, users.read, wallets.read, wallets.create, wallets:transactions.create, wallets:transactions.sign, wallets:balance.read, wallets.fund
  3. Replace your test API key with the production key

Learn More