Skip to main content
expo-native-storage is a drop-in replacement for AsyncStorage with zero breaking changes. This guide will help you migrate your existing AsyncStorage code.

Why migrate?

Migrating to expo-native-storage provides:
  • 15x faster on Android (up to 32x with scale)
  • 19x smaller bundle size (19.6KB vs 381KB)
  • Sync API for immediate access without Promises
  • Same API - minimal code changes required

Quick migration

The simplest migration is a direct import replacement:

Before (AsyncStorage)

import AsyncStorage from '@react-native-async-storage/async-storage';

await AsyncStorage.setItem('key', 'value');
const value = await AsyncStorage.getItem('key');
await AsyncStorage.removeItem('key');
await AsyncStorage.clear();

After (expo-native-storage)

import Storage from 'expo-native-storage';

await Storage.setItem('key', 'value');
const value = await Storage.getItem('key');
await Storage.removeItem('key');
await Storage.clear();
Simply replace the import statement. All method signatures are identical.

Step-by-step migration

1

Install expo-native-storage

Add expo-native-storage to your project:
npx expo install expo-native-storage
2

Rebuild your app

expo-native-storage requires native code, so rebuild your app:
npx expo prebuild --clean
npx expo run:ios  # or run:android
3

Replace imports

Update all AsyncStorage imports in your codebase:
// Before
import AsyncStorage from '@react-native-async-storage/async-storage';

// After
import Storage from 'expo-native-storage';
Use find-and-replace in your editor to update all files at once:
  • Find: from '@react-native-async-storage/async-storage'
  • Replace: from 'expo-native-storage'
4

Update method calls

Replace AsyncStorage with Storage in all method calls:
// Before
await AsyncStorage.setItem('key', 'value');
const value = await AsyncStorage.getItem('key');

// After
await Storage.setItem('key', 'value');
const value = await Storage.getItem('key');
Use find-and-replace:
  • Find: AsyncStorage.
  • Replace: Storage.
5

Test your app

Run your app and verify all storage operations work correctly:
// Test basic operations
await Storage.setItem('test', 'value');
const result = await Storage.getItem('test');
console.log(result); // Should log 'value'
6

Remove AsyncStorage (optional)

Once migration is complete, uninstall AsyncStorage:
npm uninstall @react-native-async-storage/async-storage

API compatibility

expo-native-storage implements all common AsyncStorage methods:

Fully compatible methods

AsyncStorage Methodexpo-native-storageNotes
setItem(key, value)setItem(key, value)Identical
getItem(key)getItem(key)Identical
removeItem(key)removeItem(key)Identical
clear()clear()Identical
multiSet(items)multiSet(items)Identical
multiGet(keys)multiGet(keys)Identical

Additional methods in expo-native-storage

expo-native-storage provides extra functionality:
MethodDescription
setObject(key, object)Store objects directly (auto JSON.stringify)
getObject<T>(key)Retrieve objects with type safety
setItemSync(key, value)Synchronous string storage
getItemSync(key)Synchronous string retrieval
setObjectSync(key, object)Synchronous object storage
getObjectSync<T>(key)Synchronous object retrieval
removeItemSync(key)Synchronous item removal
clearSync()Synchronous clear all

Migration examples

Example 1: User authentication

import AsyncStorage from '@react-native-async-storage/async-storage';

const login = async (token: string) => {
  await AsyncStorage.setItem('authToken', token);
};

const getToken = async () => {
  return await AsyncStorage.getItem('authToken');
};

const logout = async () => {
  await AsyncStorage.removeItem('authToken');
};

Example 2: User preferences

import AsyncStorage from '@react-native-async-storage/async-storage';

const savePreferences = async (prefs: UserPrefs) => {
  await AsyncStorage.setItem('preferences', JSON.stringify(prefs));
};

const loadPreferences = async () => {
  const json = await AsyncStorage.getItem('preferences');
  return json ? JSON.parse(json) : null;
};
expo-native-storage’s setObject and getObject methods handle JSON serialization automatically, eliminating boilerplate code.

Example 3: Theme persistence with sync API

import AsyncStorage from '@react-native-async-storage/async-storage';

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    AsyncStorage.getItem('theme').then(savedTheme => {
      if (savedTheme) setTheme(savedTheme);
      setLoading(false);
    });
  }, []);

  const updateTheme = async (newTheme: string) => {
    setTheme(newTheme);
    await AsyncStorage.setItem('theme', newTheme);
  };

  if (loading) return <LoadingScreen />;
  
  return <ThemeContext.Provider value={{ theme, updateTheme }}>{children}</ThemeContext.Provider>;
}
The sync API eliminates loading states and reduces code complexity by providing immediate access to stored values.

Data migration

Your existing AsyncStorage data will not automatically transfer to expo-native-storage. To migrate existing data:
migration.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import Storage from 'expo-native-storage';

export async function migrateData() {
  try {
    // Get all keys from AsyncStorage
    const keys = await AsyncStorage.getAllKeys();
    
    // Migrate each key
    for (const key of keys) {
      const value = await AsyncStorage.getItem(key);
      if (value !== null) {
        await Storage.setItem(key, value);
      }
    }
    
    console.log(`Migrated ${keys.length} keys from AsyncStorage`);
    
    // Optional: Clear AsyncStorage after successful migration
    // await AsyncStorage.clear();
  } catch (error) {
    console.error('Migration failed:', error);
  }
}
Run the migration once on app startup:
App.tsx
import { useEffect, useState } from 'react';
import { migrateData } from './migration';

export default function App() {
  const [isMigrated, setIsMigrated] = useState(false);

  useEffect(() => {
    const migrate = async () => {
      // Check if already migrated
      const migrated = Storage.getItemSync('migration_complete');
      
      if (!migrated) {
        await migrateData();
        Storage.setItemSync('migration_complete', 'true');
      }
      
      setIsMigrated(true);
    };

    migrate();
  }, []);

  if (!isMigrated) return <LoadingScreen />;

  return <MainApp />;
}

Breaking changes

expo-native-storage has no breaking changes from AsyncStorage for common operations. However, note these differences:
AsyncStorage’s getAllKeys() is not currently available in expo-native-storage.Workaround: Track keys manually:
// Store a list of keys
const keys = Storage.getObjectSync<string[]>('_keys') || [];

// Add key when storing
const setItemTracked = async (key: string, value: string) => {
  await Storage.setItem(key, value);
  const keys = Storage.getObjectSync<string[]>('_keys') || [];
  if (!keys.includes(key)) {
    Storage.setObjectSync('_keys', [...keys, key]);
  }
};
AsyncStorage’s mergeItem() is not available.Workaround: Read, merge, and write manually:
const mergeObject = async (key: string, updates: Record<string, any>) => {
  const existing = await Storage.getObject(key) || {};
  const merged = { ...existing, ...updates };
  await Storage.setObject(key, merged);
};
expo-native-storage uses platform-native storage (UserDefaults/SharedPreferences) while AsyncStorage uses SQLite.Impact: Existing AsyncStorage data won’t automatically appear in expo-native-storage. Use the data migration script above if needed.

Next steps

Best Practices

Learn patterns for effective storage usage.

Troubleshooting

Resolve common migration issues.

Build docs developers (and LLMs) love