Local Storage: AsyncStorage
Web apps have localStorage built right into the browser — a simple, synchronous
key-value store. React Native has no built-in equivalent. Instead, the standard
solution is a community-maintained package, @react-native-async-storage/async-storage,
which gives you a very similar key-value API — with one crucial difference covered below: every
single operation is asynchronous.
Installing AsyncStorage
AsyncStorage isn't part of core React Native — it's installed separately, like most native capabilities:
npm install @react-native-async-storage/async-storage
Then import it wherever you need it:
import AsyncStorage from '@react-native-async-storage/async-storage';
Everything Is Asynchronous
This is the single most important thing to internalize about AsyncStorage: unlike the web's
localStorage.getItem(key), which returns a value immediately, every
AsyncStorage method returns a Promise. You always need async/await
(covered in the previous lesson,
Async/Await & Error Handling) or
.then() to actually get the value out.
Storing and Reading a String Value
Writing a value is done with setItem, which takes a key and a string:
async function saveUsername() {
await AsyncStorage.setItem('username', 'alice');
}
Reading it back uses getItem, which resolves with the stored string:
async function loadUsername() {
const value = await AsyncStorage.getItem('username');
console.log(value); // 'alice'
}
getItem resolves with null — not undefined. Always check with if (value !== null) or a similar explicit check, rather than assuming a falsy check catches every "not set" case the way you might with plain JavaScript object properties.Removing a stored value is a single call to removeItem:
await AsyncStorage.removeItem('username');
Storing a Whole Object: JSON.stringify and JSON.parse
AsyncStorage only stores strings — there's no method that accepts a plain
object directly. To store anything more complex than a single string, serialize it with
JSON.stringify() before saving, and parse it back with JSON.parse()
after reading:
async function savePreferences(preferences) {
await AsyncStorage.setItem('userPreferences', JSON.stringify(preferences));
}
async function loadPreferences() {
const raw = await AsyncStorage.getItem('userPreferences');
return raw !== null ? JSON.parse(raw) : null;
}
// usage
await savePreferences({ theme: 'dark', fontSize: 16, notificationsEnabled: true });
const preferences = await loadPreferences();
console.log(preferences?.theme); // 'dark'
A Full Worked Example: A "Remember Me" Login Flow
A very common real-world use of AsyncStorage: saving an email address after a successful login,
then pre-filling it automatically the next time the app opens, using
useEffect to load the saved value when the component mounts:
import React, { useState, useEffect } from 'react';
import { View, TextInput, Button } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// pre-fill the saved email on the next app launch
useEffect(() => {
async function loadSavedEmail() {
const savedEmail = await AsyncStorage.getItem('rememberedEmail');
if (savedEmail !== null) {
setEmail(savedEmail);
}
}
loadSavedEmail();
}, []);
async function handleLogin() {
const success = await performLogin(email, password); // your own auth call
if (success) {
await AsyncStorage.setItem('rememberedEmail', email);
}
}
return (
<View>
<TextInput value={email} onChangeText={setEmail} placeholder="Email" />
<TextInput value={password} onChangeText={setPassword} placeholder="Password" secureTextEntry />
<Button title="Log In" onPress={handleLogin} />
</View>
);
}
The next time the app is opened, useEffect runs on mount, finds the previously
saved email, and calls setEmail — so the field is already filled in before the user
touches anything.
When AsyncStorage Isn't the Right Tool
AsyncStorage is designed for small amounts of simple key-value data — settings, flags, small
cached objects. It's not meant to hold large datasets, and it has no query language: there's no
way to ask it "give me all items where price is under 20" the way a real database can. If your
app needs to store and query a genuinely large or structured dataset — hundreds of records,
relationships between them, filtering and sorting — reach for a proper embedded database instead,
such as react-native-sqlite-storage (a real SQL database on-device) or
WatermelonDB (a reactive database built specifically for React Native apps with
large datasets). Both are outside the scope of this lesson, but worth knowing exist once
AsyncStorage starts to feel like the wrong shape for your data.
Security Note: AsyncStorage Is Not Encrypted
react-native-keychain (uses the OS-level
Keychain on iOS and Keystore on Android) or, if you're using Expo, expo-secure-store.
Both encrypt data at rest using the operating system's own secure storage mechanisms — something
plain AsyncStorage was never designed to do.