This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Expo SecureStore iconExpo SecureStore

A library that provides a way to encrypt and securely store key-value pairs locally on the device.

Android
iOS
tvOS
Included in Expo Go
Recommended version:
~56.0.4

expo-secure-store provides a way to encrypt and securely store key-value pairs locally on the device. Each Expo project has a separate storage system and has no access to the storage of other Expo projects.

Large payloads can be rejected by the underlying platform. Historically, some iOS releases refused values above roughly 2048 bytes. Expo does not enforce a limit, so make sure to handle native errors if you plan to store very large strings.

The requireAuthentication option is not supported in Expo Go when biometric authentication is available due to a missing NSFaceIDUsageDescription key.

Installation

Terminal
- npx expo install expo-secure-store
- yarn expo install expo-secure-store
- pnpm expo install expo-secure-store
- bun expo install expo-secure-store

If you are installing this in an existing React Native app, make sure to install expo in your project.

Configuration in app config

You can configure expo-secure-store using its built-in config plugin if you use config plugins in your project (Continuous Native Generation (CNG)). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does not use CNG, then you'll need to manually configure the library.

Example app.json with config plugin

app.json
{ "expo": { "plugins": [ [ "expo-secure-store", { "configureAndroidBackup": true, "faceIDPermission": "Allow $(PRODUCT_NAME) to access your Face ID biometric data." } ] ] } }

Configurable properties

NameDefaultDescription
configureAndroidBackuptrue
Only for: 
Android

A boolean indicating whether to configure automatic Android backup to work correctly with expo-secure-store. Learn more.

faceIDPermission"Allow $(PRODUCT_NAME) to access your Face ID biometric data."
Only for: 
iOS

A string to set the NSFaceIDUsageDescription permission message.

Are you using this library in an existing React Native app?

Add NSFaceIDUsageDescription key to Info.plist:

Info.plist
<key>NSFaceIDUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your Face ID biometric data.</string>

Platform value storage

Android

On Android, values are stored in SharedPreferences, encrypted with Android's Keystore system.

iOS

On iOS, values are stored using the keychain services as kSecClassGenericPassword. Due to the underlying nature of iOS Keychain, data stored with expo-secure-store will persist across app uninstallations when the app is reinstalled with the same bundle ID. This is an expected behavior of the iOS Keychain system and should be considered when designing your app's data handling. iOS has the additional option of being able to set the value's kSecAttrAccessible attribute, which controls when the value is available to be fetched.

Data persistence

expo-secure-store is designed to provide a persistent data storage solution across app restarts and updates. However, it is important not to rely on it as a single source of truth for irreplaceable, critical data.

  • On Android: Data saved using expo-secure-store will not be preserved upon app uninstallation.
  • On iOS: Data saved using expo-secure-store will persist across app uninstallations if the app is reinstalled with the same bundle ID. This is due to how the iOS Keychain manages stored credentials. Keep in mind that this is not guaranteed and you should never rely on this implementation detail.

Additionally, any data protected with the requireAuthentication option set to true will become inaccessible if there are changes to the user's biometric settings, such as adding a new fingerprint.

Exempting encryption prompt

Apple App Store Connect prompts you to select the type of encryption algorithm your app implements. This is known as Export Compliance Information. It is asked when publishing the app or submitting for TestFlight.

When using expo-secure-store, you can set the ios.config.usesNonExemptEncryption property to false in the app config:

app.json
{ "expo": { "ios": { "config": { "usesNonExemptEncryption": false } %%placeholder-start%%... %%placeholder-end%% } } }

Setting this property automatically handles the compliance information prompt.

Android Auto Backup

Android Auto Backup for Apps automatically backs up a user's data from apps that target and run on Android 6.0 (API level 23) or higher.

The Auto Backup system has to be configured to exclude expo-secure-store shared preferences entries, as it's impossible to decrypt them after restoring the backup — app's entries are deleted from the Android Key Store when the app is uninstalled.

If your app doesn't have any custom backup configuration, expo-secure-store will automatically configure the Auto Backup system to ignore the expo-secure-store data.

If you are using your own Auto Backup configuration, you should exclude the SecureStore under the sharedpref domain and set the configureAndroidBackup to false in the config plugin configuration.

<!-- Auto Backup configuration for Android 12 and higher --> <data-extraction-rules> <cloud-backup> <include domain="sharedpref" path="."/> <exclude domain="sharedpref" path="SecureStore"/> </cloud-backup> <device-transfer> <include domain="sharedpref" path="."/> <exclude domain="sharedpref" path="SecureStore"/> </device-transfer> </data-extraction-rules>
<!-- Auto Backup configuration for Android 11 and lower --> <full-backup-content> <include domain="sharedpref" path="."/> <exclude domain="sharedpref" path="SecureStore"/> </full-backup-content>

Usage

SecureStore
import { useState } from 'react'; import { Text, View, StyleSheet, TextInput, Button } from 'react-native'; import * as SecureStore from 'expo-secure-store'; async function save(key, value) { await SecureStore.setItemAsync(key, value); } async function getValueFor(key) { let result = await SecureStore.getItemAsync(key); if (result) { alert("🔐 Here's your value 🔐 \n" + result); } else { alert('No values stored under that key.'); } } export default function App() { const [key, onChangeKey] = useState('Your key here'); const [value, onChangeValue] = useState('Your value here'); return ( <View style={styles.container}> <Text style={styles.paragraph}>Save an item, and grab it later!</Text> {%%placeholder-start%%Add some TextInput components... %%placeholder-end%%} <TextInput style={styles.textInput} clearTextOnFocus onChangeText={text => onChangeKey(text)} value={key} /> <TextInput style={styles.textInput} clearTextOnFocus onChangeText={text => onChangeValue(text)} value={value} /> {} <Button title="Save this key/value pair" onPress={() => { save(key, value); onChangeKey('Your key here'); onChangeValue('Your value here'); }} /> <Text style={styles.paragraph}>🔐 Enter your key 🔐</Text> <TextInput style={styles.textInput} onSubmitEditing={event => { getValueFor(event.nativeEvent.text); }} placeholder="Enter the key for the value you want to get" /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingTop: 10, backgroundColor: '#ecf0f1', padding: 8, }, paragraph: { marginTop: 34, margin: 24, fontSize: 18, fontWeight: 'bold', textAlign: 'center', }, textInput: { height: 35, borderColor: 'gray', borderWidth: 0.5, padding: 4, }, });

API

import * as SecureStore from 'expo-secure-store';

No API data file found, sorry!