apps/docs/content/guides/getting-started/tutorials/with-expo-react-native.mdx
<$Partial path="quickstart_intro.mdx" />
<Admonition type="note">If you get stuck while working through this guide, refer to the full example on GitHub.
</Admonition><$Partial path="project_setup.mdx" variables={{ "framework": "exporeactnative", "tab": "mobiles" }} />
Start by building the React Native app from scratch.
Use expo to initialize
an app called expo-user-management:
npx create-expo-app -t expo-template-blank-typescript expo-user-management
cd expo-user-management
Then install the additional dependencies:
npx expo install @supabase/supabase-js @rneui/themed expo-sqlite
Now create a helper file to initialize the Supabase client using the API URL and the key that you copied earlier.
These variables are safe to expose in your Expo app since Supabase has Row Level Security enabled on your Database.
<Tabs scrollable size="large" type="underlined" defaultActiveId="local-storage" queryGroup="auth-store"
<TabPanel id="local-storage" label="LocalStorage">
<$CodeSample
path="/user-management/expo-user-management/lib/supabase.ts"
lines={[[1, -1]]}
meta="name=lib/supabase.ts"
/>
If you wish to encrypt the user's session information, you can use `aes-js` and store the encryption key in [Expo SecureStore](https://docs.expo.dev/versions/latest/sdk/securestore). The [`aes-js` library](https://github.com/ricmoo/aes-js) is a reputable JavaScript-only implementation of the AES encryption algorithm in CTR mode. A new 256-bit encryption key is generated using the `react-native-get-random-values` library. This key is stored inside Expo's SecureStore, while the value is encrypted and placed inside AsyncStorage.
Make sure that:
- You keep the `expo-secure-storage`, `aes-js` and `react-native-get-random-values` libraries up-to-date.
- Choose the correct [`SecureStoreOptions`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestoreoptions) for your app's needs. E.g. [`SecureStore.WHEN_UNLOCKED`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestorewhen_unlocked) regulates when the data can be accessed.
- Carefully consider optimizations or other modifications to the above example, as those can lead to introducing subtle security vulnerabilities.
Install the necessary dependencies in the root of your Expo project:
```bash
npm install @supabase/supabase-js
npm install @rneui/themed @react-native-async-storage/async-storage
npm install aes-js react-native-get-random-values
npm install --save-dev @types/aes-js
npx expo install expo-secure-store
```
Implement a `LargeSecureStore` class to pass in as Auth storage for the `supabase-js` client:
<$CodeTabs>
```ts name=lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from 'expo-secure-store';
import * as aesjs from 'aes-js';
import 'react-native-get-random-values';
// As Expo's SecureStore does not support values larger than 2048
// bytes, an AES-256 key is generated and stored in SecureStore, while
// it is used to encrypt/decrypt values stored in AsyncStorage.
class LargeSecureStore {
private async _encrypt(key: string, value: string) {
const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));
const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));
await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));
return aesjs.utils.hex.fromBytes(encryptedBytes);
}
private async _decrypt(key: string, value: string) {
const encryptionKeyHex = await SecureStore.getItemAsync(key);
if (!encryptionKeyHex) {
return encryptionKeyHex;
}
const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));
return aesjs.utils.utf8.fromBytes(decryptedBytes);
}
async getItem(key: string) {
const encrypted = await AsyncStorage.getItem(key);
if (!encrypted) { return encrypted; }
return await this._decrypt(key, encrypted);
}
async removeItem(key: string) {
await AsyncStorage.removeItem(key);
await SecureStore.deleteItemAsync(key);
}
async setItem(key: string, value: string) {
const encrypted = await this._encrypt(key, value);
await AsyncStorage.setItem(key, encrypted);
}
}
const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL
const supabasePublishableKey = YOUR_REACT_NATIVE_SUPABASE_PUBLISHABLE_KEY
const supabase = createClient(supabaseUrl, supabasePublishableKey, {
auth: {
storage: new LargeSecureStore(),
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
},
});
```
</$CodeTabs>
Set up a React Native component to manage logins and sign ups. Users should be able to sign in with their email and password.
<$CodeSample path="/user-management/expo-user-management/components/Auth.tsx" lines={[[1, -1]]} meta="name=components/Auth.tsx" />
<Admonition type="note">By default Supabase Auth requires email verification before a session is created for the users. To support email verification you need to implement deep link handling!
While testing, you can disable email confirmation in your project's email auth provider settings.
</Admonition>After a user signs in, you can let them to edit their profile details and manage their account.
Create a new component for that called Account.tsx.
<$CodeSample path="/user-management/expo-user-management/components/Account.tsx" lines={[[1, -1]]} meta="name=components/Account.tsx" />
Now that you have all the components in place, update App.tsx:
<$CodeSample path="/user-management/expo-user-management/App.tsx" lines={[[1, -1]]} meta="name=App.tsx" />
Once that's done, run this in a terminal window:
npm start
And then press the appropriate key for the environment you want to test the app in and you should see the completed app.
Every Supabase project is configured with Storage for managing large files like photos and videos.
You need an image picker that works on the environment you are building the project for, this example uses expo-image-picker.
npx expo install expo-image-picker
Create an avatar for the user so that they can upload a profile photo. Start by creating a new component:
<$CodeSample path="/user-management/expo-user-management/components/Avatar.tsx" lines={[[1, -1]]} meta="name=components/Avatar.tsx" />
And then add the widget to the Account page. The Account.tsx component shown earlier already includes the Avatar component when using the full example code.
Now run the prebuild command to get the application working on your chosen platform.
npx expo prebuild
At this stage you have a fully functional application!