RORK LABJP
PLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updatesPLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updates
Articles/Dev Tools
Dev Tools/2026-03-29Intermediate

Local Data Persistence in Rork Apps — Choosing AsyncStorage, MMKV, and SQLite by Measurement

A measured comparison of local storage in Rork apps: AsyncStorage, MMKV, and SQLite. Covers a zero-downtime migration, MMKV pitfalls, and how transactions plus WAL make SQLite bulk inserts dozens of times faster — grounded in hands-on indie development.

Rork547AsyncStorage10MMKV6SQLite3Local StorageData Persistence2React Native234

Premium Article

The night a list screen froze for a moment

I had added a favorites feature to one of the apps I run as an indie developer. Everything worked smoothly up to a few hundred entries. Then a heavy user's favorites grew into the thousands, and reports came in: every time they opened the list, the screen flashed white for a beat.

The cause was that, on launch, the app loaded the entire favorites list from AsyncStorage and ran JSON.parse on one enormous array every single time. The data was just key-value, yet startup got heavier as the data grew. Back then I had told myself "storage is something I can figure out later," and that complacency came back to me as a visible drop in perceived speed.

Local storage choices don't matter while an app is small — anything works. The difference shows up once a user's data has grown. That's exactly why understanding the mechanics up front saves you from rebuilding later.

This guide compares the three staples available in React Native / Expo-based Rork apps — AsyncStorage, MMKV, and SQLite — and goes deeper than a feature tour: into why speed appears or doesn't, how to migrate safely, the pitfalls to avoid, and how to turn a "slow SQLite" into a fast one. All of it is grounded in things I actually tripped over.

AsyncStorage — The Shortest Path to Something That Works

AsyncStorage is the community-maintained key-value store for React Native. It works out of the box in projects Rork generates, and its learning curve is close to zero.

  • Data format: String key-value pairs (objects via JSON.stringify)
  • API: Fully Promise-based and asynchronous
  • Backend: SQLite on Android (6MB default cap), plist files on iOS
  • Thread safety: Internally serialized, safe under concurrent access
import AsyncStorage from '@react-native-async-storage/async-storage';
 
// Save user preferences
const saveUserPreferences = async (preferences: {
  theme: 'light' | 'dark';
  language: string;
  notifications: boolean;
}) => {
  try {
    const jsonValue = JSON.stringify(preferences);
    await AsyncStorage.setItem('@user_preferences', jsonValue);
    console.log('Preferences saved successfully');
  } catch (error) {
    console.error('Failed to save preferences:', error);
  }
};
 
// Load user preferences
const loadUserPreferences = async () => {
  try {
    const jsonValue = await AsyncStorage.getItem('@user_preferences');
    if (jsonValue !== null) {
      return JSON.parse(jsonValue);
    }
    return { theme: 'light', language: 'en', notifications: true };
  } catch (error) {
    console.error('Failed to load preferences:', error);
    return null;
  }
};
 
// Expected output:
// saveUserPreferences({ theme: 'dark', language: 'en', notifications: true })
// => "Preferences saved successfully"
// loadUserPreferences()
// => { theme: 'dark', language: 'en', notifications: true }

For settings, flags, temporary token storage, a few dozen cached entries, and MVP-stage apps, this is plenty. I personally spend the first few weeks of a new app almost entirely on AsyncStorage — in the phase where I want fast iteration, I'd rather not sink time into storage design.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Why AsyncStorage slows down as data grows, plus a one-time migration to MMKV with no data loss
Making SQLite bulk inserts dozens of times faster with transactions and WAL, with measured behavior
A quick decision flow to pick AsyncStorage / MMKV / SQLite from the nature of your data
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-05-19
Fixing 'Row too big to fit into CursorWindow' on Android When AsyncStorage Holds Too Much in Rork
When a React Native app generated with Rork stores large JSON or image metadata in AsyncStorage, Android can throw a Row too big to fit into CursorWindow exception. Here are the practical fixes — MMKV migration, chunked keys, payload trimming, and compression — explained from real wallpaper-app experience.
Dev Tools2026-04-20
Rork App Data Not Saving or Disappearing: Causes and Fixes
When Rork app data isn't saving or disappears after a restart, a handful of root causes explain most cases. This guide covers AsyncStorage pitfalls, async timing bugs, key mismatches, and when to switch to MMKV.
Dev Tools2026-06-22
When a Poisoned Cache Crashes Your App on Every Launch — Designing a Safe-Mode Boot Your Users Can Escape On Their Own
When a persisted cache goes bad and the app crashes at the same spot on every launch, the only option left to the user is to reinstall. This article designs a safe-mode boot for Expo (React Native): the app counts its own early crashes, confirms a launch only once it becomes interactive, and resets just the dangerous state in graduated steps.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →