ReadyNative

MMKV

Set up MMKV for storage in an Expo app with ReadyNative: fastest sync key/value (Nitro) · dev build required (no Expo Go)

Expo Go: no - dev build required

Ships native code. Make a dev build with bunx expo run:ios / bunx expo run:android (or bunx eas-cli build --profile development).

MMKV is the fastest option: a synchronous, memory-mapped key/value store on react-native-mmkv 4 (Nitro), behind the same src/lib/storage.ts. I'd pick it when you already need a dev build for something else (Unistyles, Sentry, RevenueCat…) and want stores and zustand persist to hydrate before first paint with no async fallback. Stay on kv-store if Expo Go matters, and choose async-storage only for a library that hard-requires it. Callers don't change either way: they import storage from @/lib/storage.

Setup

bun run setup --storage mmkv
  1. Make a dev build - react-native-mmkv is autolinked native code: npx expo run:ios or npx expo run:android (or eas build --profile development). Expo Go will not load it.

Setup writes src/lib/storage.ts (createMmkvStorage() with the instance id readynative), adds react-native-mmkv ^4.3.2 and react-native-nitro-modules 0.37.1 (the same pin as ui/unistyles), a __mocks__/react-native-nitro-modules.ts jest stand-in, and a round-trip test. There's no config plugin.

Usage

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

storage.getItemSync?.("readynative:draft"); // always present with MMKV
storage.setItemSync?.("readynative:draft", JSON.stringify(draft));
await storage.setItem("readynative:draft", JSON.stringify(draft)); // async methods wrap the sync ones

The built-in stores, zustand persist / Jotai atomWithAppStorage and i18n all go through the adapter, so switching to MMKV needs no caller changes - they stop awaiting.

Gotchas

  • Dev build required. bun run doctor flags it if you're on Expo Go.
  • react-native-nitro-modules throws on import without the native runtime, hence the __mocks__ file. If you add another Nitro library, extend that mock or add your own jest.mock.
  • Each createMMKV({ id }) is its own file. Don't change MMKV_INSTANCE_ID after shipping or stored data becomes invisible.
  • Web falls back to localStorage (web isn't a gate).
  • Swapping stacks: bun run setup --storage kv-store|async-storage replaces src/lib/storage.ts. Keys stay the same readynative:* strings but the data lives in a different file, so existing values do not move - users start fresh. No migration ships.

Remove it

Every app needs a storage option, so there is no none to fall back to - you switch instead. While modules/ exists (a tree set up with --keep-modules), one call does it:

bun run setup --storage kv-store --yes --keep-modules

A finalized tree can't switch: clone a fresh copy of your tier repo, run setup there with the new option and port your code across - see Can I change a module after setup?.

Reference

Everything below is generated from modules/storage/mmkv/module.json - the same file bun run setup reads, so it is what actually lands in your repo.

Install

bun run setup --storage mmkv

Module id: storage/mmkv.

Dependencies

PackageVersionKind
react-native-mmkv^4.3.2dependency
react-native-nitro-modules0.37.1dependency

Privacy

Required-reason APIs: UserDefaults (CA92.1).

Doctor checks

  • dev build toolchain (Xcode / Android SDK)

After setup

  1. MMKV 4 is a native (Nitro) module: make a dev build with npx expo run:ios / npx expo run:android (or eas build --profile development). Expo Go will not load it.

Files

3 files copied to the project root
  • __mocks__/react-native-nitro-modules.ts
  • src/lib/__tests__/storage-mmkv.test.ts
  • src/lib/storage.ts

On this page

Get ReadyNative