Skip to content

Conversation

krizzu
Copy link
Member

@krizzu krizzu commented Sep 13, 2025

AsyncStorage v3

Intro

I’ve been maintaining AsyncStorage for a while, and the growing issues have become a real burden. As I see it, the problems fall into a few categories:

  • Inconsistent implementation across native platforms — Each platform uses different ways to persist data (SQLite on Android, file-based on iOS/macOS, LocalStorage on Web).
  • Single shared storage — The app and any third-party SDKs using AsyncStorage are writing into the same database/table/storage. If someone calls .clear(), everything is gone — not just app data, but SDK data too.
  • Missing features, such as:
    • Hook implementation that works and doesn’t suck
    • Access to database files directly (for export, backup, inspection)

All of this makes AsyncStorage harder to evolve and less reliable than it should be. My goal is to improve this library in terms of usage and maintenance.

Goals

  • Unify the backend — Use SQLite consistently on all native platforms. On Web, use IndexedDB.
  • Support multiple databases — Apps and SDKs should be able to create their own isolated stores.
  • Allow access to stored values from native side (brownfield apps).
  • Keep the API compatible with Web Storage API.
  • Make errors more predictable.
  • Make sure it works with Expo.
  • Try to make migration to v3 smooth and with minimal effort for users.

Todo / Progress

Things to do before this is ready to go.

General

  • Decide how many RN versions to support. Follow Meta’s approach (support 3 versions back from the current)?
  • Make sure the npm tarball includes Maven local and apple frameworks.

Repo

  • Renamed default-storage package back to async-storage.
  • Dropped sqlite-storage and api packages. API was just a single interface, so it was moved to async-storage.
  • Repo is now a Gradle project, with shared-storage as its only module. This is a Kotlin Multiplatform library for accessing the database.
  • Dropped e2e tests — they weren’t running on CI anyway, so this needs to be revisited later.
  • Separated library implementation from example apps. We currently have two examples: React Native and Compose Multiplatform (Android and iOS). They live in the examples directory.
  • Bumped versions of Yarn, Node, TypeScript, ESLint, Prettier.
  • Added macOS support to example-react-native.
  • Added web example to examples/example-web.
  • Update GH actions
    • Run TS/Lint tests on PR
    • Run native ui tests on PR if files changed
    • Build and deploy on version bump
  • Improve Examples to show off all functionality
    • single value crud
    • multiple value crud
    • keys and storage clearance

JavaScript API

  • API is compatible with Web Storage API, with a few additions for multi requests (getMany, setMany, removeMany).
  • (breaking) multiGet, multiSet, and multiRemove renamed to getMany, setMany, and removeMany. Their API also changed from array-based ([key, value]) to object-based ({ [key]: value }).
  • (breaking) mergeItem and multiMerge removed due to differences in implementation across platforms. The merging logic is best decided by the end user.
  • New AsyncStorage instances are created via a createStorage method, where a database name must be provided.
  • AsyncStorageError error is thrown from AsyncStorage methods.
  • Hook implementation:
    • Remove it now and revisit after the v3 release

Native

  • All native platforms (Android, iOS, macOS, Windows) now use SQLite via Room database (Kotlin Multiplatform).
  • shared-storage is built with KMP and consumed by the Native Module for React Native. Android uses local Maven repository, iOS uses xcframework via CocoaPods.
  • (potentially breaking) Changed the name of the Native Module to RNAsyncStorage.
  • Added logging around important events, such as storage creation and exceptions.
  • Added database files utility to find db files easier (DatabaseFiles).
  • Shared storage now throws SqliteStorage errors to be handled by RN native module.
  • Check if SQLDelight supports windows native target and if it’s feasible to swap implementation with it

Android

  • Android implementation works via Maven local — make sure it works outside the example app.
  • Module reworked and now uses Room from shared-storage.
  • (potentially breaking) Package renamed to org.asyncstorage from com.reactnativecommunity.asyncstorage.
  • Handle exceptions in the RN module.
  • v2 backward compatibility - exposed default export is a proxy to old storage (AsyncStorage v2)

iOS / macOS

  • Module reworked and now uses Room from shared-storage.
  • (breaking) Dropped delegate support for brownfield access.
  • (breaking) Removed the flag to opt out of iCloud backup exclusion. Databases directory is excluded by default now.
  • Handle exceptions in the RN module.
  • v2 backward compatibility - exposed default export is a proxy to old storage (AsyncStorage v2)

Windows

  • Add a proxy to use current implementation
    • Because KMP room does not support windows native target, the v3 will simply proxy back to legacy implementation with a warning.

Web

  • (breaking) Storage is now backed by IndexedDB (via idb package).
  • v2 backward compatibility - exposed default export is a proxy to old storage (AsyncStorage v2)

Documentation

  • Migrate web docs to MkDocs.
  • Redo README to reflect the latest changes (installation, usage).
  • Document about db location:
    • Database name should not have .db or .sqlite extensions (database file gets .sqlite extension)
    • Grouped in async-storage/databases directory
    • Created database files live in $platform_db_directory/async-storage/databases/$databaseName/ directory, where $databaseName.sqlite files are
  • Document about error thrown from AsyncStorage (AsyncStorageError) and describe error types
  • Document migration from v2
    • Legacy Storage (old data) is accessible via default export from the library.
    • The API has been migrated to the AsyncStorage API (link it)
    • mergeItem/multiMerge has been removed
  • Document how to access native storage from brownfield

Migration from v2

  • Legacy storage for Apple
  • Legacy storage for Android
  • Legacy storage Migration for Web
  • Legacy Storage (previous data) is accessible via default export
  • (idea) Leave the old implementation as the default export in JS.
    • Create a proxy so the default AsyncStorage uses the old implementation per platform. This would allow a gradual migration.


Feel free to comment here with suggestions/ideas and let me know what you think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant