> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native

> Create user wallets from your Expo app in under 5 minutes

<Note>
  **This page has been updated for Wallets SDK V1.** If you are using the previous version,
  see the [previous version of this page](/wallets/v0/quickstarts/react-native) or the [V1 migration guide](/wallets/guides/migrate-to-v1).
</Note>

<CardGroup cols={2}>
  <Snippet file="before-you-start.mdx" />

  <Card title="Expo Wallets Quickstart" icon="github" iconType="duotone" href="https://github.com/Crossmint/wallets-expo-quickstart">
    See a full working example.
  </Card>
</CardGroup>

<Steps>
  <Step title="Install the SDK">
    Run the following command to install the SDK:

    <Snippet file="client-sdk-react-native-ui-installation-cmd.mdx" />
  </Step>

  <Step title="Add the Crossmint providers to your app">
    Add the necessary Crossmint providers to your app. This example uses [Crossmint Auth](/authentication/introduction)
    but you can use [any authentication provider of your choice](/wallets/guides/bring-your-own-auth).

    With the current setup, a wallet will be created automatically on login.

    See all supported chains [here](/introduction/supported-chains).

    ```tsx providers.tsx theme={null}
    import {
        CrossmintProvider,
        CrossmintAuthProvider,
        CrossmintWalletProvider,
    } from "@crossmint/client-sdk-react-native-ui";

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

    export default function CrossmintProviders(
        { children }: ProvidersProps
    ) {
        return (
            <CrossmintProvider
                apiKey={process.env.EXPO_PUBLIC_CROSSMINT_API_KEY!}
            >
                <CrossmintAuthProvider>
                    <CrossmintWalletProvider
                        createOnLogin={{
                            chain: "base-sepolia",
                            recovery: { type: "email" },
                        }}
                    >
                        {children}
                    </CrossmintWalletProvider>
                </CrossmintAuthProvider>
            </CrossmintProvider>
        );
    }
    ```

    For detailed configuration options, see the [React Native SDK Reference](/sdk-reference/wallets/react-native/providers#crossmintwalletprovider).
  </Step>

  <Step title="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.

    ```tsx index.tsx theme={null}
    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
        const url = Linking.useURL();

        // Handle OAuth callback
        useEffect(() => {
            if (url != null) {
                createAuthSession(url);
            }
        }, [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) {
                console.error("sendOtp error:", error);
                const message =
                    error instanceof Error
                        ? error.message
                        : "Failed to send OTP.";
                Alert.alert("Error", message);
            } 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) {
                console.error("verifyOtp error:", error);
                const message =
                    error instanceof Error
                        ? error.message
                        : "Invalid OTP. Try again.";
                Alert.alert("Error", message);
            } 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>
        );
    }
    ```
  </Step>
</Steps>

## Launching in Production

For production, some changes are required:

1. Create a developer account on the <a href="https://www.crossmint.com/console" target="_blank">production console</a>
2. Create a production client API key on the <a href="https://www.crossmint.com/console/projects/apiKeys" target="_blank">API Keys</a> 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
4. **Use your own authentication provider**: For production applications, Crossmint recommends using [third-party authentication](/wallets/guides/bring-your-own-auth) with providers like Auth0, Firebase, or Supabase, rather than Crossmint Auth. Configure JWT authentication in the <a href="https://www.crossmint.com/console/projects/apiKeys" target="_blank">Crossmint Console</a> under API Keys > JWT Authentication.

## Learn More

<CardGroup cols={3}>
  <Card title="Check Balances" icon="money-bill-transfer" iconType="duotone" href="/wallets/guides/check-balances">
    Check the balance of a wallet.
  </Card>

  <Card title="Transfer Tokens" icon="coins" iconType="duotone" color="#1A5785" href="/wallets/guides/transfer-tokens">
    Send tokens between wallets.
  </Card>

  <Card title="Operational Signers" icon="key" iconType="duotone" color="#2156B9" href="/wallets/guides/signers/add-signers">
    Register operational signers on a wallet.
  </Card>
</CardGroup>

## Other Links

<CardGroup cols={2}>
  <Card title="API Reference" icon="terminal" color="#B56710" href="/api-reference/wallets/create-wallet">
    Deep dive into API reference docs.
  </Card>

  <Card title="Talk to an expert" icon="message" iconType="duotone" color="#ADD8E6" href="https://www.crossmint.com/contact/sales">
    Contact our sales team for support.
  </Card>
</CardGroup>
