docs/faq.md
AsyncStorage only stores strings. Serialize values with JSON.stringify before writing, and JSON.parse when reading:
import { createAsyncStorage } from "@react-native-async-storage/async-storage";
const storage = createAsyncStorage("my-app");
// Storing an object
const user = { name: "John", age: 30 };
await storage.setItem("user", JSON.stringify(user));
// Reading it back
const raw = await storage.getItem("user");
const user = raw ? JSON.parse(raw) : null;
Yes. The default export is a backward-compatible v2 instance:
import AsyncStorage from "@react-native-async-storage/async-storage";
await AsyncStorage.getItem("key");
This uses the same legacy API as v2, including multiGet, multiSet, etc. If you are starting a new integration,
prefer createAsyncStorage instead. See the Migration guide for the full API comparison.
The v3 AsyncStorage instance implements exactly the storage interface Redux Persist expects (getItem, setItem,
removeItem), so you can pass an instance directly as the storage option:
import { createAsyncStorage } from "@react-native-async-storage/async-storage";
import { persistReducer, persistStore } from "redux-persist";
import { combineReducers, createStore } from "redux";
const storage = createAsyncStorage("redux");
const persistConfig = {
key: "root",
storage,
};
const rootReducer = combineReducers({
// your reducers
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer);
const persistor = persistStore(store);
!!! tip "Use default export for migration from v2"
The default export (import AsyncStorage from "...") implements the same interface and can be passed as storage too.
It allows for gradual migration to v3.
TanStack Query's @tanstack/query-async-storage-persister package expects a storage object with getItem, setItem,
and removeItem. The v3 AsyncStorage instance matches this interface directly:
import { createAsyncStorage } from "@react-native-async-storage/async-storage";
import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persister";
import { QueryClient } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
});
const storage = createAsyncStorage("tanstack-query");
const persister = createAsyncStoragePersister({ storage });
export default function App() {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
<YourApp />
</PersistQueryClientProvider>
);
}
!!! note "Required packages"
Install @tanstack/query-async-storage-persister and @tanstack/react-query-persist-client separately.
Zustand's persist middleware accepts a custom storage adapter via createJSONStorage. Pass a factory function
returning an AsyncStorage instance:
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import { createAsyncStorage } from "@react-native-async-storage/async-storage";
const storage = createAsyncStorage("zustand");
interface BearState {
bears: number;
addBear: () => void;
reset: () => void;
}
export const useBearStore = create<BearState>()(
persist(
(set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
reset: () => set({ bears: 0 }),
}),
{
name: "bear-storage",
storage: createJSONStorage(() => storage),
}
)
);
createJSONStorage handles serialization, so you don't need to manually call JSON.stringify / JSON.parse.
All AsyncStorage methods throw AsyncStorageError on failure. Use a try/catch block and inspect e.type to handle
specific failure modes:
import {
AsyncStorageError,
createAsyncStorage,
} from "@react-native-async-storage/async-storage";
const storage = createAsyncStorage("my-app");
try {
await storage.setItem("key", "value");
} catch (e) {
if (e instanceof AsyncStorageError) {
console.error(`Storage error [${e.type}]: ${e.message}`);
}
}
See the Error handling guide for the full list of error types and what each one means.
Batch operations (setMany, getMany, removeMany) are atomic. If any part of the operation fails, the entire batch
is rolled back and an AsyncStorageError is thrown. No partial writes will be committed.