# react-native-mmkv-storage puts Tencent MMKV behind a JSI storage API

> This React Native library exposes MMKV through a C++ JSI binding, so reads and writes stay off the bridge. It adds encryption, reactive hooks and a transaction manager on top of a vendored copy of the Tencent library, and it works on both the old and new React Native architectures with different version floors.

**ammarahm-ed/react-native-mmkv-storage** — An ultra fast and encrypted mobile key-value storage for React Native

- Repository: https://github.com/ammarahm-ed/react-native-mmkv-storage
- Website: https://rnmmkv.vercel.app
- Stars: 1,754 · Forks: 118
- Language: C++
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/ammarahm-ed-react-native-mmkv-storage

## A storage layer that never touches the bridge

react-native-mmkv-storage exists because the usual way React Native apps persist data has a structural cost. The library wraps Tencent MMKV and exposes it through React Native JSI, which means the JavaScript side calls into native code directly instead of serialising a request, crossing the bridge, waiting for a promise and crossing back. The project documentation makes the claim plainly, that JSI makes this the fastest storage option for React Native, and the mechanism is what matters more than the claim.

Underneath, MMKV is memory mapped. The documentation explains that MMKV uses mmap to keep memory synced with the file and protobuf to encode and decode values, which is why reads and writes avoid the read, parse, write, flush pattern of a disk store. Tencent developed MMKV for their WeChat app, and the project links the upstream Android and iOS benchmark pages rather than publishing its own numbers, which is the honest way to handle performance claims.

The library was rewritten in C++ for both Android and iOS starting from v0.5.0, and it weighs about 50K on Android and 30K on iOS before packaging. The audience is any team shipping a mobile app that keeps user state on the device, with two specific pulls beyond raw speed: you can create many separate database instances with different encryption settings, and you can subscribe to changes so a component updates the moment a value changes.

## The version floor depends on which React Native architecture you run

This is the first thing to check, and the answer is not the same for every app. The documentation splits support into two branches. The new architecture, which is the default since React Native 0.76 and the only mode since React Native 0.82, requires React Native 0.75 or above and is tested up to React Native 0.87. The old architecture requires React Native 0.71.0 and above, and the guidance for anyone on an older version of React Native is to keep using 0.8.x.

The reason the new architecture is not a problem is worth stating, because it is unusual. The documentation says codegen specs are generated at build time by your app, so the library always matches your React Native version. There is no pinned native spec to drift out of date, which removes one of the classic failure modes of a JSI library.

The same rule applies to Expo, with one caveat. The package contains native code, so it does not work in Expo Go, and the documented path for the Expo bare workflow is to run prebuild, which generates the native projects so the module can be linked. The build pipelines in the repository are separate for Android and iOS, and the package exposes a podspec for the iOS side.

Practically, that means a team on an old React Native version can still use the library, but on a frozen branch, while a team on a current version is on the actively shipped line.

## Installing it and reading a value back

The install is a single npm command, and the Expo bare workflow needs one more step so the native project exists:

```bash
npm install react-native-mmkv-storage
```

```bash
expo prebuild
```

After that, the entry point is the MMKVLoader class. Creating a store and reading a value through the reactive hook looks like this:

```js
import { MMKVLoader, useMMKVStorage } from 'react-native-mmkv-storage';

const storage = new MMKVLoader().initialize();
const App = () => {
  const [user, setUser] = useMMKVStorage('user', storage, 'robert');
  const [age, setAge] = useMMKVStorage('age', storage, 24);

  return (
    <View style={styles.header}>
      <Text style={styles.headerText}>
        I am {user} and I am {age} years old.
      </Text>
    </View>
  );
};
```

The third argument is the default value, so the hook returns 'robert' and 24 before anything has been written. Writing through the setter persists the value, which is the point: the documentation describes the hook as a persisted state that always writes every change to storage and updates the app UI instantly, and the value survives a reload or a restart of the app.

If you want a separate store rather than a second key, MMKVLoader instances are cheap. The documentation argues that many small instances beat one large one, since separate modules in an app can use data differently and a single database slows down as it grows:

```js
const userStorage = new MMKVLoader().withEncryption().withInstanceID('userdata').initialize();

const settingsStorage = new MMKVLoader().withInstanceID('settings').initialize();
```

Multiple instances are also how you isolate an encrypted store from a plain one, which is a common real requirement.

## Encryption is a random key held in Keychain or Keystore

Full encryption is supported on both platforms and the documentation names the cipher: AES CFB-128. Enabling it is a single call on the loader, with a comment in the example that marks what it does:

```js
const storage = new MMKVLoader()
  .withEncryption() // Generates a random key and stores it securely in Keychain
```

The key handling is the part to understand before you rely on it. The library generates a random key and stores it securely, using Keychain on iOS and Android Keystore on Android, and the documentation notes that the Keystore route needs API 23 and above. You can also choose to store the encryption key yourself for continuous use, which matters if your app needs to decrypt a store from a background process or a share extension where the keychain is not reachable in the same way.

The practical consequence is that the key is a device side secret rather than something derived from a password. If a user signs in on two devices, the two stores are not automatically the same, and if a device is wiped, the store is unrecoverable even if you kept a backup of the app data. The documentation does not describe a key rotation procedure or a way to re-encrypt an existing instance in place, so treat the key as part of the design rather than a detail.

Encryption is per instance. A plain instance next to an encrypted one is not a contradiction, it is just a different loader configuration.

## Transactions let you maintain a secondary index in a hook

The transaction manager is the least obvious feature and the one that solves a real problem. You register lifecycle functions on a storage instance, such as Read, Write and Delete, and they run when a value changes. The documentation frames this as a way to listen to a value's lifecycle and mutate it on the fly, and as the mechanism for building custom indexes in a few lines.

The example registers a beforewrite handler for the 'object' type and maintains a tag based index:

```js
storage.transactions.register('object', 'beforewrite', (key, value) => {
  if (key.startsWith('post.')) {
    const indexKey = `${value.tag}-index`;
    const index = storage.getArray(indexKey) || [];
    storage.setArray(indexKey, [...index, key]);
  }
  return value;
});
```

The handler receives the key and the value and returns the value, so it can transform what gets stored. What is interesting is the combination with the second hook. useIndex takes an array of keys and returns the array of values for those keys, and it is meant to work with transactions: you keep the key list in one storage entry, the objects in others, and the index hook listens to read and write changes and updates the values accordingly. The usage is short:

```jsx
import { FlatList, Text } from 'react-native';
import { useIndex, useMMKVStorage } from 'react-native-mmkv-storage';

const App = () => {
  const [postIds] = useMMKVStorage('postsIndex', storage, []);
  const [posts] = useIndex(postIds, 'object', storage);

  return (
    <FlatList
      data={posts}
      keyExtractor={(item, index) => `${index}`}
      renderItem={({ item }) => <Text>{item.title}</Text>}
    />
  );
};
```

The trade-off is honest. You are maintaining a derived structure by hand, and the correctness of the index depends on your handler running for every write path, including writes that bypass the hook.

## What the repository shows about testing and the vendored MMKV copy

The layout explains a lot about how the project is built. The MMKV directory at the top of the repository is a vendored copy of the upstream Tencent library, and the package scripts include mmkv:update to run scripts/update-mmkv.sh and mmkv:check to run the same script with the --check flag, so the vendored copy can be refreshed and verified. The prepack script runs scripts/check-mmkv-assets.sh, which means the asset check happens when the package is packed rather than only in continuous integration.

The published package is TypeScript, with the build script running tsc and the main entry point at ./dist/index.js. The source sits in src, the native code in android and ios, and there is a packages directory alongside a react-native-mmkv-storage.podspec and a react-native.config.js.

Testing is the weak spot in the public picture. The npm test script in package.json is a stub that prints an error and exits, so `npm test` is not the way to check anything. The actual test material lives in the example project, which has its own __tests__ and e2e directories, a jest.config.js and a TESTING.md file, and there is a jest directory at the repository root. The example also contains a benchmarks folder.

So if you are evaluating the library, the example project is where the working usage examples and the test setup live, not the root package scripts.

## Maintenance pace, licence, and the version jump to 13

The repository is not archived and the last push was on 2026-10-18. The release history shows a long line of version numbers, with a major bump arriving at the end of the period: v12.0.1 on 2025-12-04, then v13.0.0 and v13.0.1 both on 2026-10-09, and the package.json version is 13.0.1.

A major version jump in a native module is not cosmetic. It signals a breaking change somewhere in the JavaScript API, the native build, or the supported React Native range, and the release notes for a single major bump are the first place a migrating team should look. The fact that both v13.0.0 and v13.0.1 landed on the same day suggests a quick follow up fix, which is normal practice but does not tell you what changed.

The licence is MIT, which is the permissive end of the scale and imposes no conditions on commercial use or on redistribution of the package, only the obligation to keep the licence notice. That matters here because MMKV itself is a separate project with its own terms, and the vendored copy in the MMKV directory carries those terms as well.

The documentation site is built with VitePress from the docs directory, with scripts for docs:dev, docs:build and docs:preview. The library also links a blog post on the author own site about data storage options for React Native, and a separate post on building a JSI module, which tells you where the maintainer's interest lies.

## Conclusion

react-native-mmkv-storage is the right fit for a React Native app that stores meaningful amounts of state on device and wants encryption, reactive components and several separate stores without writing native code. It is the wrong choice for an Expo Go project, since the package needs a native build, and for apps still on a React Native version below 0.71, which are pinned to the older 0.8.x line. Before you adopt it, check the architecture your app runs, since the new architecture is the only mode from React Native 0.82, and confirm that the encryption path suits you: the key is generated for you and stored in Keychain or Android Keystore, which means restoring an app to a new device needs thought about how that key travels.

## FAQ

### How do I install react-native-mmkv-storage in a React Native app?

Run npm install react-native-mmkv-storage, then create a store with the MMKVLoader class, for example new MMKVLoader().initialize(). For an Expo project using the bare workflow you also run expo prebuild, because the package contains native code and does not work inside Expo Go.

### Which React Native versions does react-native-mmkv-storage support?

The new architecture requires React Native 0.75 or above and is tested up to 0.87, and it is the only mode from React Native 0.82. The old architecture requires React Native 0.71.0 and above, and apps on older versions should stay on the 0.8.x line.

### Does react-native-mmkv-storage support encryption?

Yes, with AES CFB-128 on Android and iOS. Calling withEncryption() on the loader generates a random key and stores it in Keychain on iOS or Android Keystore on Android, which requires API 23 or above, and you can also manage the encryption key yourself.

### What is the difference between useMMKVStorage and useIndex?

useMMKVStorage persists a single value under a key and updates your UI when it changes, with a default value for the first render. useIndex takes an array of keys and returns the matching values, and it is designed to work with the transaction manager, which you use to maintain the key list as a custom index.

### How do I run the tests for this library?

The npm test script in package.json is a placeholder that exits with an error. The working test setup, including __tests__, e2e tests and a jest configuration, lives in the example project, along with a TESTING.md file and a benchmarks folder.

## Sources

- [ammarahm-ed/react-native-mmkv-storage on GitHub](https://github.com/ammarahm-ed/react-native-mmkv-storage)
- [License: MIT](https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/LICENSE)
- [Project website](https://rnmmkv.vercel.app)
- [README](https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/README.md)
- [Releases](https://github.com/ammarahm-ed/react-native-mmkv-storage/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ammarahm-ed-react-native-mmkv-storage
