React NativeMMKVAsyncStorageStorageExpo

MMKV vs AsyncStorage: Fastest Storage for React Native & Expo

MMKV vs AsyncStorage compared for React Native and Expo — speed, sync vs async APIs, encryption, setup with Nitro Modules in react-native-mmkv v4, hooks, Zustand persistence, and when to use SecureStore or SQLite instead.

DeveloperMill4 min read

Local storage is one of the first decisions in any React Native app: persisted settings, onboarding flags, cached API responses, and offline drafts. The two most common answers are AsyncStorage and MMKV. In 2026 the choice is clearer than ever, especially with react-native-mmkv v4 rebuilt on Nitro Modules.

The short answer

  • Use MMKV for app settings, feature flags, cached data, and state persistence (Zustand, Redux, TanStack Query). It's synchronous and about 30x faster.
  • Use AsyncStorage if you must run in Expo Go or want zero native setup for a prototype.
  • Use expo-secure-store for tokens and secrets.
  • Use expo-sqlite for relational or queryable data.

Why MMKV is so much faster

AsyncStorage is an asynchronous key/value store. Every read is a promise that crosses into native code, and values are serialized strings.

MMKV (originally built by WeChat) is a memory-mapped native store. react-native-mmkv exposes it through JSI, so reads and writes are synchronous. There's no await, no loading state, and no flash of default theme on startup.

// AsyncStorage
const theme = await AsyncStorage.getItem("theme") // async, needs a loading state

// MMKV
const theme = storage.getString("theme") // sync, available on first render

That synchronous read is the real UX win. You can read the user's theme, locale, or onboarding flag before the first frame renders.

Install MMKV v4 in Expo

MMKV includes native code, so it needs a development build (not Expo Go):

npx expo install react-native-mmkv react-native-nitro-modules
npx expo prebuild

Or build with EAS: eas build --profile development.

Create a storage instance

v4 replaces the new MMKV() constructor with createMMKV(). Create one instance and export it:

// lib/storage.ts
import { createMMKV } from "react-native-mmkv"

export const storage = createMMKV()

// Optional: separate, encrypted instance
export const secureCache = createMMKV({
  id: "user-cache",
  encryptionKey: "load-this-from-secure-store",
})

Read, write, and delete

import { storage } from "@/lib/storage"

storage.set("user.name", "Ada")
storage.set("user.age", 36)
storage.set("onboarding.done", true)

storage.getString("user.name")      // "Ada"
storage.getNumber("user.age")       // 36
storage.getBoolean("onboarding.done") // true

storage.contains("user.name")       // true
storage.getAllKeys()                // ["user.name", ...]
storage.remove("user.name")         // v4: renamed from delete()
storage.clearAll()

For objects, use JSON:

storage.set("cart", JSON.stringify(cart))
const cart = JSON.parse(storage.getString("cart") ?? "[]")

React hooks

MMKV ships hooks that re-render when a value changes, which works well for settings:

import { useMMKVBoolean, useMMKVString } from "react-native-mmkv"

export function ThemeToggle() {
  const [theme, setTheme] = useMMKVString("theme")
  const [haptics, setHaptics] = useMMKVBoolean("haptics")
  // setTheme("dark") persists and updates every subscriber
}

Persist Zustand with MMKV

The most common production pattern is to back your state manager with MMKV:

import { create } from "zustand"
import { persist, createJSONStorage, type StateStorage } from "zustand/middleware"
import { storage } from "@/lib/storage"

const mmkvStorage: StateStorage = {
  getItem: (name) => storage.getString(name) ?? null,
  setItem: (name, value) => storage.set(name, value),
  removeItem: (name) => storage.remove(name),
}

export const useSettings = create(
  persist<{ theme: "light" | "dark"; setTheme: (t: "light" | "dark") => void }>(
    (set) => ({ theme: "light", setTheme: (theme) => set({ theme }) }),
    { name: "settings", storage: createJSONStorage(() => mmkvStorage) },
  ),
)

Because MMKV is synchronous, the store hydrates instantly, with no "hydrating…" state. More on picking a state library in our React Native state management guide.

Migrating from AsyncStorage

Run a one-time migration on app start:

import AsyncStorage from "@react-native-async-storage/async-storage"
import { storage } from "@/lib/storage"

export async function migrateFromAsyncStorage() {
  if (storage.getBoolean("migrated.asyncstorage")) return
  const keys = await AsyncStorage.getAllKeys()
  for (const key of keys) {
    const value = await AsyncStorage.getItem(key)
    if (value != null) storage.set(key, value)
  }
  await AsyncStorage.multiRemove(keys)
  storage.set("migrated.asyncstorage", true)
}

MMKV vs AsyncStorage at a glance

  • Speed: MMKV is roughly 30x faster in benchmarks.
  • API: MMKV is synchronous; AsyncStorage is promise-based.
  • Types: MMKV stores strings, numbers, booleans, and ArrayBuffers natively; AsyncStorage stores strings only.
  • Encryption: MMKV has built-in AES (encryptionKey, encrypt(), decrypt()); AsyncStorage has none.
  • Multiple instances: MMKV supports them via id; AsyncStorage has a single store.
  • Expo Go: AsyncStorage works there; MMKV needs a dev build.

Where secrets belong

MMKV encryption protects data at rest, but your encryption key still needs a safe home. Generate it once, store it in expo-secure-store (Keychain or Keystore), and pass it to createMMKV. Keep auth tokens in SecureStore directly. Our Expo authentication guide shows the full pattern.

Want to skip the plumbing? Start from one of our Expo app templates and spend your time on features instead of setup.

Frequently asked questions

Is MMKV faster than AsyncStorage?

Yes. react-native-mmkv is roughly 30x faster than AsyncStorage in its benchmarks because it uses a memory-mapped native key/value store accessed synchronously through JSI, instead of AsyncStorage's asynchronous bridge-based API.

Does react-native-mmkv work in Expo Go?

No. MMKV v4 is built on Nitro Modules, which are custom native code not bundled in Expo Go. Use a development build: install with `npx expo install react-native-mmkv react-native-nitro-modules`, then run `npx expo prebuild` or build with EAS.

What changed in react-native-mmkv v4?

v4 is rewritten on Nitro Modules. You now create instances with `createMMKV()` instead of `new MMKV()`, the `delete()` method is renamed to `remove()`, and React Native 0.75 or newer is required.

Should I store auth tokens in MMKV?

Prefer expo-secure-store for auth tokens and secrets, because it uses the iOS Keychain and Android Keystore. MMKV supports AES encryption with an encryptionKey, but the key itself must still be stored somewhere safe — usually SecureStore.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates