
Local-First for React Native
A few years ago, the only reason to consider a local-first app architecture was if advanced offline functionality was a hard requirement for your app1. More recently though, giving users a real-time collaborative UX (like Figma) has been enticing enough for more developers to venture down the path of local-first.
In particular, the interest in building React Native apps with local-first architecture is on the rise. New tools and tutorials2 make it easier than ever to get started with a local-first React Native app. More and more developers are realizing that local-first is not simply a way to provide certain features (e.g. real-time reactive UI, offline mode, real-time collaboration), but rather an architecture that avoids a lot of the complexities that come with modern cloud-first apps (e.g. caching, optimistic updates, etc.)
Part of what makes local-first appealing for React Native is that it inherently provides a simple approach to state management. There are many good state management frameworks available like Zustand, Redux and MobX, but local-first provides a different paradigm: using a local database (e.g. SQLite) to manage state3. Coupled with tools like Kysely (a SQL query builder) and TinyBase (a reactive in-memory data store), using a local database for state management becomes even more accessible.
React Native Local Database Options
The growing interest in local-first architectures for React Native led us to further explore the various local database options in the React Native ecosystem. Evaluating each requires diving a bit deeper into their code, and also having an idea of what you’re looking for.
We created the table below to show the main options available with key features that should help you decide which local database will work best for you. Good luck!
Preface
This breakdown only consists of local database options that provide full database functionality (persistent, consistent and queryable data stores). We ignored less-featured databases and caches. For example, we haven’t included react-native-mmkv or async-storage.
All of the below databases are open-source.
Comparison Table
| Database / Package | Description | Type | Asynchronous by Default? | Concurrency | Encryption Support | Web Support | Query Method | Plug-in Cloud Sync Support | Backend Database Type |
|---|---|---|---|---|---|---|---|---|---|
| react-native-nitro-sqlite | Nitro SQLite embeds the latest version of SQLite and provides a low-level JSI-backed API to execute SQL queries. A fork of the original react-native-quick-sqlite (by ospfranco), which was deprecated in favour of OP-SQLite. | SQLite | Yes (has equivalent synchronous and asynchronous APIs for most operations) | No (single read or write transaction at a time) | No | No | SQL | No | - |
| powersync-react-native-quick-sqlite | Fork of react-native-quick-sqlite with some optimisations and enhancements. Note: This is not meant to be used directly, without PowerSync. 4 | SQLite | Yes | Yes (built-in support for one write transaction and multiple read transactions concurrently) | No (use OP-SQLite instead) | No | SQL | Yes (via PowerSync) | Postgres, MySQL, MongoDB |
| op-sqlite | Embeds the latest version of SQLite and provides a low-level API to execute SQL queries. | SQLite | Yes (has equivalent synchronous and asynchronous APIs for most operations) | Yes (multiple connections supported) | Yes (via SQLCipher) | No | SQL | Yes (via PowerSync) | - |
| expo-sqlite | Provides access to a SQLite database that can be queried through a WebSQL-like API. | SQLite | Yes (has equivalent synchronous and asynchronous APIs for most operations) | Likely yes (via WAL and manual connections) | Yes (via SQLCipher) | Yes (via WA-SQLite) | SQL | Yes (via PowerSync) | - |
| WatermelonDB | Database framework that provides high-level abstractions for SQLite. | SQLite | Yes | No (single Reader or Writer at a time) | No | Yes | ORM 5 | Yes (bring your own backend implementation) | Backend-specific |
| RxDB | NoSQL database made for JavaScript applications supporting various underlying storage layers. | NoSQL | Yes | Unknown | Yes via encryption-crypto-js | Yes | Domain-specific | Yes (e.g. with CouchDB server or any custom GraphQL endpoint) | NoSQL |
| LiveStore | LiveStore is a next-generation state management framework based on reactive SQLite and git-inspired syncing (via event-sourcing). | SQLite | No. Queries are performed against a synchronous in-memory DB. This DB is persisted asynchronously. Docs | No. Queries are executed synchronously on the main thread. | No (docs) | Yes | ORM with the ability to perform custom SQL queries. | Yes (docs) • CloudFlare Workers • ElectricSQL • S2 (planned) • Custom sync integration | • CloudFlare syncing uses D1 (SQLite compatible) to persist LiveStore events. • Postgres DB used to store mutation event history when using ElectricSQL |
| Realm (deprecated) | Object database originally maintained by Realm, then MonogoDB. | NoSQL | Yes | Unknown | Yes | Yes | Domain-specific | No (MongoDB Atlas Device Sync support deprecated) | NoSQL |
Notes On Table Columns
Type
Indicates whether the database is SQLite-based or NoSQL.
Asynchronous by Default
Indicates whether the database supports asynchronous operations; in other words, whether there is support that prevents the main thread from being blocked.
Concurrency
This refers to the database’s ability to support multiple concurrent transactions.
Encryption Support
Whether the database supports encryption, and if so, how.
Web Support
This refers to the library being compatible with plain JavaScript web or frameworks.
Query Method
Here the query methods are "SQL" or "ORM" for SQLite-based databases and "domain-specific" for NoSQL databases since they each have their own query syntax flavor.
Plug-In Cloud Sync Support
This refers to the database’s ability to plug into an existing sync system that keeps data in sync with a primary cloud database and across multiple devices.
Backend Database Type
This indicates the database type that the database sync solution supports.
Feedback
If you have feedback on this summary or have a suggestion for a feature or database to include, please let us know on our Discord server.
Footnotes
-
The concept of “offline-first” is a predecessor to “local-first” and there’s overlap between the two concepts. ↩
-
See an example here. ↩
-
See a previous post on local-first state management with SQLite for more on that. ↩
-
This will be available as a general-purpose SQL library in the near future. ↩
-
SQL is possible but discouraged. ↩
