In react-native-mmkv v4, how do createMMKV's id and encryptionKey options give you separate and encrypted instances, and where should the key come from?
answer
- default id mmkv.default
- one id, one storage file
- AES-128 default, 16-byte key
- encryptionType AES-256, 32 bytes
- key from Keychain or Keystore, not bundle
basics
~20 sEach createMMKV id is a separate storage, so user data and app settings live apart and can be wiped separately. encryptionKey encrypts an instance (AES-128 by default); generate the key per install and keep it in platform secure storage, never in code.
solid answer
~40 s`createMMKV()` opens the default instance, id `mmkv.default`. `createMMKV({ id: 'user-42' })` opens a separate storage with its own keys, so on sign-out I can `clearAll()` it or remove it with `deleteMMKV('user-42')` while app-wide settings survive. Adding `encryptionKey` encrypts that instance: AES-128 by default with a key of at most 16 bytes, or `encryptionType: 'AES-256'` with up to 32 bytes; a longer key makes creation throw. The same key must be supplied every time the instance is opened, or reads fail. The key must not be a string in the JavaScript bundle, because the bundle ships inside the app and can be extracted. I generate a random key on first launch and keep it in Keychain- or Keystore-backed secure storage. On the web, `encryptionKey` is not supported and throws.
code
typescript · 23 linesimport { createMMKV, deleteMMKV, existsMMKV } from "react-native-mmkv";
import type { MMKV } from "react-native-mmkv";
// storedKey is read from platform secure storage by the caller; null when none exists yet.
// makeKey returns a random key of at most 16 bytes (AES-128, the default).
export function openUserStorage(
userId: string,
storedKey: string | null,
makeKey: () => string
): { storage: MMKV; key: string } {
const id = `user-${userId}`;
let key = storedKey;
if (key === null) {
// Without the old key an existing encrypted file is unreadable, so start clean.
if (existsMMKV(id)) deleteMMKV(id);
key = makeKey();
}
return { storage: createMMKV({ id, encryptionKey: key }), key };
}
export function signOut(userId: string): void {
deleteMMKV(`user-${userId}`);
}go deeper
Recall that createMMKV takes an id for separate storage, that the default id is mmkv.default, and that encryptionKey turns on encryption.
Explain the key-length limits for AES-128 and AES-256, why the same key is needed on every open, and how separate ids enable targeted wipes on sign-out.
Design key handling: a random per-install key in platform secure storage, a recovery path when the key is gone, and deleteMMKV for per-user teardown.
Decide which data warrants encrypted storage and its key-management cost, and set the team rule for what may live in plaintext instances.
## Instances are identified by id `createMMKV(configuration?)` returns an MMKV instance. Without a configuration it uses the default id, **`mmkv.default`**. With a configuration you pass an `id`, and each distinct id is a **separate storage** with its own keys and its own file on disk: ```typescript import { createMMKV } from "react-native-mmkv"; export const appStorage = createMMKV({ id: "app-settings" }); export const userStorage = (userId: string) => createMMKV({ id: `user-${userId}` }); ``` The library's own examples use this split: global settings in one instance, the signed-in user's data in another. Benefits: - **No key collisions** between features or users. - **Targeted wipes**: `clearAll()` on the user instance at sign-out leaves the language and theme alone. - **Whole-instance removal**: `deleteMMKV(id)` deletes an instance and `existsMMKV(id)` checks for one. The README recommends creating an instance once and exporting it. Inside components, `useMMKV(configuration)` creates an instance and only recreates it when the configuration's fields change, so the object does not need to be memoised. ## Other configuration fields | Field | Default | What it does | |---|---|---| | `id` | `mmkv.default` | names the storage | | `path` | `$(Documents)/mmkv/` per the docs | custom root directory | | `encryptionKey` | none (plaintext) | encrypts the instance | | `encryptionType` | `AES-128` | or `AES-256` | | `mode` | `single-process` | `multi-process` for extensions or app groups | | `readOnly` | `false` | any `set` throws | | `compareBeforeSet` | `false` | skip writes of unchanged values | ## How encryption works in practice By default MMKV writes values in **plain text** and relies on the operating system's sandbox. Supplying `encryptionKey` changes that for one instance: 1. **Key length is checked at creation.** With the default AES-128 the key may be at most 16 bytes; with `encryptionType: "AES-256"`, at most 32 bytes. A longer key makes `createMMKV` throw. 2. **The key is needed on every open.** Open the instance with a different key or type and reads fail. Losing the key means losing the data. 3. **Existing instances can be converted.** `storage.encrypt(key, type?)` encrypts a plaintext instance or re-keys an encrypted one; `storage.decrypt()` turns it back into plaintext; `storage.isEncrypted` reports the state. 4. **Web has no encryption.** On React Native Web, passing `encryptionKey` (or `path`) throws. ## Where the key must come from Encryption is only as strong as the secrecy of the key. Common mistakes, and what to do instead: - **A string literal in source code** ends up in the JavaScript bundle, which ships inside every copy of the app. Anyone can unpack it, so the encryption protects nothing. - **A value derived from the device or app version** can be recomputed by anyone who knows the recipe. - **The right pattern:** generate a random key on first launch, store it in the platform's secure storage (the iOS Keychain or an Android Keystore-backed store, reached through a secure-storage library), read it at startup, and pass it to `createMMKV`. Plan for the key disappearing, for example after a restore onto a new device where the secure-storage entry is not carried over. The encrypted instance can then no longer be read, so when the key is missing the app should delete any existing instance for that id with `deleteMMKV`, issue a new key and start fresh rather than crash. ## Sharing with extensions On iOS an app can share an instance with its widgets or extensions through an **App Group**. When the `Info.plist` contains an `AppGroupIdentifier` key and no `path` is given, the library stores the instance in the app-group directory automatically. Such instances should be created with `mode: "multi-process"`, because another process may change the file; the library reloads external changes when the app becomes active. Two other flags are worth knowing: `readOnly: true` makes every `set` throw, which suits an extension that only reads, and `compareBeforeSet: true` skips disk writes of unchanged values. ## When to use which instance In a to-do app, the list itself and the "show completed" toggle can sit in a plaintext per-user instance, since the operating system sandbox already keeps other apps out. Encryption is worth its key-management cost when the stored data would hurt someone if the files were copied from a rooted phone. Whether a credential belongs in an encrypted MMKV instance or directly in platform secure storage is a security-design decision beyond this library's API.
- What happens if you open an existing encrypted MMKV instance with a different key?The library documents that future opens need the same key and encryption type, otherwise reads fail. The data is effectively lost unless the original key is found, so the app should treat this as a reset: delete the instance with `deleteMMKV` and rebuild it, rather than crash on launch.
- How do you wipe one user's data on sign-out without touching app-wide settings?Keep the user's data in its own instance, for example `createMMKV({ id: 'user-42' })`, and call `clearAll()` on it or `deleteMMKV('user-42')`. The default or settings instance, with the language and theme, is a different id and is unaffected.
saying these in an interview costs you the question
- Hard-coding the MMKV encryptionKey as a string in the JavaScript source
- Assuming separate MMKV ids share one key space
- Thinking a 32-byte key works with the default AES-128 setting
- Believing MMKV encrypts by default without an encryptionKey
- Expecting encryptionKey to work on React Native Web