You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First of all I already saw #1011 but it couldn't help me.
Recently I tried upgrading a bare app with epxo modules to expo 55 and rn 83 from 54 and 81. However after getting the app running again mmkv doesn't persist the data. I was now debugging this problem for three days but still have no clue on solving this.
I also tried creating a smaller example app, but no matter what i did it was always working.
I tried:
Removing custom plugins
Removing the appIdSuffix
Upgrading to the latest mmkv version
removing the scr folder
copying the android folder in a new app
build the project on a different pc
build it on an emulator
build it on an actual phone
...
But still the new app was always working and the original wasn't.
I don't know if I have hit some edgecase or just do something wrong, but I am out of ideas and hope someone here can help me.
Also what I noticed is that mmkv doesn't create a new folder or read the existing one. Other packages like op-sqlite continue working normally, even though they write in the same folder.
New features, bugfixes, updates and other improvements are all handled mostly by @mrousavy in his free time.
To support @mrousavy, please consider 💖 sponsoring him on GitHub 💖.
Sponsored issues will be prioritized.
I see you've spent three days debugging MMKV persistence after upgrading to Expo 55 / RN 0.83 — and frustratingly, it works fine in a fresh app but not in the original. The fact that op-sqlite continues working in the same directory while MMKV doesn't even create its folder suggests something in your native build config or module resolution changed during the upgrade.
I'm a full-stack developer working across Python, C#, and TypeScript daily — building everything from REST APIs and database systems to desktop applications and automation pipelines.
What I deliver:
Clean, tested code with proactive communication — no surprises
Experience with JavaScript and React Native debugging in production environments
Fast turnaround — I typically have a working first version within days, not weeks
For this issue:
I'd start by diffing your android/build.gradle, gradle.properties, and AndroidManifest.xml against the working sample app to isolate what's breaking MMKV's native module initialization. Then trace the actual file I/O calls to see where MMKV thinks it should write versus where your app's sandboxed storage lives.
Can start immediately. Have you checked whether MMKV's native module is actually loading, or does MMKV.initialize() silently fail?
I encountered the issue ‘MMKV does not persist data’ after I upgraded my Expo project to Expo 55:
"expo": "55.0.5",
"react": "19.2.0",
"react-native": "0.83.2",
"react-native-mmkv": "4.3.0",
"react-native-nitro-modules": "0.35.2",
... ,
and here is my solution that i use like react-native-mmkv+4.3.0.patch:
this helped, but more precise testing is needed in different cases, and it would be great to hear thought on this from @mrousavy , thanks in advance, and I will be glad if this helps someone.
Hey @CD-Z , Thanks for the issue. The issue is not in react-native-mmkv but in how ENVs are read .
react-native-dotenv is setting JEST_WORKER_ID to be a truthy value which makes createMMKV to run the Mock env .
For quick fix you can patch dot env to something like below .
--- a/node_modules/react-native-dotenv/index.js
+++ b/node_modules/react-native-dotenv/index.js
@@ -152,7 +152,9 @@ module.exports = (api, options) => {
const key = path.toComputedKey()
if (t.isStringLiteral(key)) {
const importedId = key.value
- const value = (env && importedId in env) ? env[importedId] : process.env[importedId]
+ // FIX: Only inline values from .env files, never fall back to build-time process.env
+ // (build-time process.env leaks JEST_WORKER_ID from Metro's jest-worker)
+ const value = (env && importedId in env) ? env[importedId] : undefined
if (value !== undefined) {
path.replaceWith(t.valueToNode(value))
}
Hey @riteshshukla04, thank you very much for your help. I didn't even think it could be an issue with env variables. I changed from react-native-dotenv to react-native-config as dotenv hasn't been updated in 2 years and I don't want to deal with patch files. Now it is working perfectly fine again.
Maybe react-native-mmkv should change the isTest method to something more robust like @devappsstudio suggested or add a warning log that it is now in test mode. For this reason I will keep the issue open even though my problem is solved.
I know and I didn't mean to demand anything but in my opinion this is something that should be decided by someone who is used to this project and knows how to solve this situation without creating new problems.
After chatting about this a bit I decided to close this issue - this is not related to react-native-mmkv and should probably be fixed in the dotenv package
@mrousavy i had the exact same issue with the storage not being persisted after upgrading the expo version, mmkv pkg version did not change, i migrated from the react-native-dotenv to expo native env solution with EXPO_PUBLIC prefix
ref doc: https://docs.expo.dev/guides/environment-variables/
and the issue persisted i had to make the same patch as @riteshshukla04 and add a patch script
I just want to point out that that the fix (process.env.JEST_WORKER_ID != null || process.env.VITEST_WORKER_ID != null) && process.env.NODE_ENV === 'test' is not correct . Its sad that LLM generates this.
I just want to point out that that the fix (process.env.JEST_WORKER_ID != null || process.env.VITEST_WORKER_ID != null) && process.env.NODE_ENV === 'test' is not correct . Its sad that LLM generates this.
Same root cause as #1011 — I posted the full write-up there, short version here since this issue describes it precisely ("MMKV didn't create a new folder or read the existing one, while op-sqlite kept working in the same folder"). That is exactly the signature: the native store is never touched at all.
isTest() returns true in ordinary Expo builds, so createMMKV() returns createMockMMKV() — an in-memory Map. Reads and writes succeed, getAllKeys() works, no file is ever created, and everything is empty on next launch.
Why:
Metro parallelises transforms with jest-worker, which sets JEST_WORKER_ID in each worker process.
babel-preset-expo inlines static process.env.X member expressions at transform time, including inside node_modules.
The transform worker's id is therefore baked into the app bundle as a literal.
"6" is Metro transform worker #6. JEST_WORKER_ID is unset in the shell, in .env*, and in the Metro process — it only exists inside the transform worker.
Corroborating: with MMKV_logLevel=0, MMKV's core logs initialisation but never an instance — no loadFromFile, no mmap, no open fd — and existsMMKV(id) is false for every store. Both Nitro's Logger and MMKV's core are silent at default log levels, which is why this is so hard to pin down.
Fix — read the env through a variable so Babel cannot inline it:
Jest still sets JEST_WORKER_ID at runtime, so genuine test runs keep the mock. Verified on device (Expo 57.0.15 / RN 0.86.2 / mmkv 4.3.2 / metro 0.84.5): all stores now create <id> + <id>.crc, and after a force-stop + relaunch, loaded [base-storage] with 10 key-values / loaded [redux-storage] with 1 key-values. Before the patch they were empty on every launch.
This affects any Expo app on mmkv v4, in debug and release alike, and it fails silently.
# 1. edit both files in node_modules# node_modules/react-native-mmkv/lib/isTest.js (this is what gets bundled)# node_modules/react-native-mmkv/src/isTest.ts (keep in sync)# 2. generate the patch
npx patch-package react-native-mmkv
# 3. ensure it re-applies on install# package.json -> "scripts": { "postinstall": "patch-package" }# 4. rebuild native + clear Metro cache
npx react-native start --reset-cache
lib/isTest.js is the one that matters — package.json has "main": "lib/index", so that is what Metro bundles. Patching only src/isTest.ts will appear to do nothing.
Two gotchas worth flagging:
Clear the Metro cache. Editing files inside node_modules does not reliably invalidate the transform cache, so the old (mocked) module can keep being served.
Do not verify with globalThis.process?.env?.JEST_WORKER_ID. Optional chaining defeats Babel's inlining, so it reads undefined at runtime while the library's static read sees the baked-in worker id. I chased that false negative for hours. Check by grepping the served bundle instead:
I just want to point out that that the fix (process.env.JEST_WORKER_ID != null || process.env.VITEST_WORKER_ID != null) && process.env.NODE_ENV === 'test' is not correct . Its sad that LLM generates this.
Activity
maintenance-hans commentedon Mar 30, 2026
Guten Tag, Hans here.
Note
New features, bugfixes, updates and other improvements are all handled mostly by
@mrousavyin his free time.To support
@mrousavy, please consider 💖 sponsoring him on GitHub 💖.Sponsored issues will be prioritized.
WeberG619 commentedon Mar 30, 2026
Hi,
I see you've spent three days debugging MMKV persistence after upgrading to Expo 55 / RN 0.83 — and frustratingly, it works fine in a fresh app but not in the original. The fact that op-sqlite continues working in the same directory while MMKV doesn't even create its folder suggests something in your native build config or module resolution changed during the upgrade.
I'm a full-stack developer working across Python, C#, and TypeScript daily — building everything from REST APIs and database systems to desktop applications and automation pipelines.
What I deliver:
For this issue:
I'd start by diffing your android/build.gradle, gradle.properties, and AndroidManifest.xml against the working sample app to isolate what's breaking MMKV's native module initialization. Then trace the actual file I/O calls to see where MMKV thinks it should write versus where your app's sandboxed storage lives.
Can start immediately. Have you checked whether MMKV's native module is actually loading, or does
MMKV.initialize()silently fail?Weber Gouin
devappsstudio commentedon Mar 31, 2026
Hi,
I encountered the issue ‘MMKV does not persist data’ after I upgraded my Expo project to Expo 55:
"expo": "55.0.5",
"react": "19.2.0",
"react-native": "0.83.2",
"react-native-mmkv": "4.3.0",
"react-native-nitro-modules": "0.35.2",
... ,
and here is my solution that i use like react-native-mmkv+4.3.0.patch:
this helped, but more precise testing is needed in different cases, and it would be great to hear thought on this from @mrousavy , thanks in advance, and I will be glad if this helps someone.
riteshshukla04 commentedon Mar 31, 2026
Hey @CD-Z , Thanks for the issue. The issue is not in
react-native-mmkvbut in how ENVs are read .react-native-dotenvis setting JEST_WORKER_ID to be a truthy value which makes createMMKV to run the Mock env .For quick fix you can patch dot env to something like below .
CD-Z commentedon Mar 31, 2026
Hey @riteshshukla04, thank you very much for your help. I didn't even think it could be an issue with env variables. I changed from react-native-dotenv to react-native-config as dotenv hasn't been updated in 2 years and I don't want to deal with patch files. Now it is working perfectly fine again.
Maybe react-native-mmkv should change the isTest method to something more robust like @devappsstudio suggested or add a warning log that it is now in test mode. For this reason I will keep the issue open even though my problem is solved.
mrousavy commentedon Mar 31, 2026
react-native-mmkv is open source
CD-Z commentedon Mar 31, 2026
I know and I didn't mean to demand anything but in my opinion this is something that should be decided by someone who is used to this project and knows how to solve this situation without creating new problems.
mrousavy commentedon Apr 1, 2026
Makes sense!
After chatting about this a bit I decided to close this issue - this is not related to react-native-mmkv and should probably be fixed in the dotenv package
kKaskak commentedon May 23, 2026
@mrousavy i had the exact same issue with the storage not being persisted after upgrading the expo version, mmkv pkg version did not change, i migrated from the
react-native-dotenvto expo native env solution withEXPO_PUBLICprefixref doc: https://docs.expo.dev/guides/environment-variables/
and the issue persisted i had to make the same patch as @riteshshukla04 and add a patch script
mrousavy commentedon May 23, 2026
Can you reproduce this via a Harness test in this repo?
kKaskak commentedon May 23, 2026
investigated and i could not reproduce, i rebuild everything again and the patch was not necessary anymore, probably cache issue
thank you all works well now without the
react-native-dotenvpackageriteshshukla04 commentedon May 23, 2026
I just want to point out that that the fix
(process.env.JEST_WORKER_ID != null || process.env.VITEST_WORKER_ID != null) && process.env.NODE_ENV === 'test'is not correct . Its sad that LLM generates this.This is how expo determines the testing env .
https://github.com/expo/expo/blob/31d4a6cc20eb3c1c6c015ed30b02b9b1b3119769/packages/expo-notifications/src/index.ts#L3-L5
NODE_ENV may or may not be test in JEST
kKaskak commentedon May 23, 2026
To get it working before removing the package I had to apply your suggestion yes, the other one did not work. Thank you I tagged the wrong person lol
praisedavid787 commentedon Sep 3, 2026
Same root cause as #1011 — I posted the full write-up there, short version here since this issue describes it precisely ("MMKV didn't create a new folder or read the existing one, while op-sqlite kept working in the same folder"). That is exactly the signature: the native store is never touched at all.
isTest()returnstruein ordinary Expo builds, socreateMMKV()returnscreateMockMMKV()— an in-memoryMap. Reads and writes succeed,getAllKeys()works, no file is ever created, and everything is empty on next launch.Why:
jest-worker, which setsJEST_WORKER_IDin each worker process.babel-preset-expoinlines staticprocess.env.Xmember expressions at transform time, including insidenode_modules.Grepping the compiled bundle for
isTest:"6"is Metro transform worker #6.JEST_WORKER_IDis unset in the shell, in.env*, and in the Metro process — it only exists inside the transform worker.Corroborating: with
MMKV_logLevel=0, MMKV's core logs initialisation but never an instance — noloadFromFile, nommap, noopen fd— andexistsMMKV(id)isfalsefor every store. Both Nitro'sLoggerand MMKV's core are silent at default log levels, which is why this is so hard to pin down.Fix — read the env through a variable so Babel cannot inline it:
Jest still sets
JEST_WORKER_IDat runtime, so genuine test runs keep the mock. Verified on device (Expo 57.0.15 / RN 0.86.2 / mmkv 4.3.2 / metro 0.84.5): all stores now create<id>+<id>.crc, and after a force-stop + relaunch,loaded [base-storage] with 10 key-values/loaded [redux-storage] with 1 key-values. Before the patch they were empty on every launch.This affects any Expo app on mmkv v4, in debug and release alike, and it fails silently.
Applying this today (until it's fixed upstream)
With
patch-package:lib/isTest.jsis the one that matters —package.jsonhas"main": "lib/index", so that is what Metro bundles. Patching onlysrc/isTest.tswill appear to do nothing.Two gotchas worth flagging:
node_modulesdoes not reliably invalidate the transform cache, so the old (mocked) module can keep being served.globalThis.process?.env?.JEST_WORKER_ID. Optional chaining defeats Babel's inlining, so it readsundefinedat runtime while the library's static read sees the baked-in worker id. I chased that false negative for hours. Check by grepping the served bundle instead:If you see
Boolean("<number>")or"<number>" != null, you are getting the mock.mrousavy commentedon Sep 7, 2026
Doesn't react-native-reanimated do the same check? That process.env. stuff seems hacky?
riteshshukla04 commentedon Sep 7, 2026
Adding to what Marc mentioned ,Please read this comment . Even expo does the same like MMKV . This shouldn’t land in Mmkv but fixed in maybe dotenv