SQLite as in-app database

Resources for developers using SQLite as an embedded client-side database.
SQLite
SQLite is the most widely deployed database in the world. It’s small, fast and has a lot of built-in functionality, making it ideal as an in-app database. It's hard to beat when it comes to performance, flexibility and advanced functionality (such as aggregations, joins, advanced indexing and JSON support).

This page aims to help developers get up to speed with using SQLite as a highly performant embedded client-side database.

Helpful articles

The Current State of SQLite Persistence for Web Apps
  • Key factors in comparing SQLite web implementations

  • Comparison of leading implementations and VFSs supported

  • Recommendations on best implementations to use

Read the article
SQLite Optimizations for High-Performance
  • Expected baseline performance/throughput

  • 10 key performance optimizations to consider

  • Performance optimizations included in our sqlite_async library

Read the article
Local-First State Management with SQLite
  • Using a SQLite database to manage state

  • Example to-do list app

  • Link to video talk on this topic

Read the article
Flutter Database Comparison
  • Background on performance benchmarking methodology used

  • Performance comparison of sqlite_async, sqflite, Isar and ObjectBox

  • Link to GitHub project with performance benchmarking code

Read the article

Recommended Client-Side SQLite Libraries

We've evaluated available SQLite libraries based on three criteria:

Configurability refers to the library’s flexibility in allowing developers to customize its behavior and functionality to meet specific application needs. This includes access to compiler options, using a good set of default extensions and catering for extension loading.

Consistency ensures the library has consistent options across various platforms, capabilities should be consistent across platforms with sensible default settings and cross-platform SDKs should directly access the C API without converting to another language first.

Capability evaluates the libraries inclusion of a comprehensive transaction API, exposure of all possible parameter types and good to have functionality such as update hooks (used for watching for changes), prepared statements and creating user-defined functions.

Recommend libraries for frameworks

Flutter + sqlite_async

We recommend sqlite_async because it wraps the sqlite3 Dart bindings and configures the database with a good set of defaults, with all database calls being asynchronous to avoid blocking the UI.

While it does not expose compiler options, it does cater for loading various extensions. It also ensures that core SQLite functionality is consistent across all platforms supported by the library.

For performance-sensitive tasks, transactions are asynchronous and concurrent by default (multiple read and single write) by using a pool of connections, each running in a separate Isolate. Allows the use of SQL queries directly, making complex queries easier, and does not require any code generation. Allows access to the underlying sqlite3 library when high-performance low-level access is required. Allows the use of Drift when an ORM is required.

View library

Web + wa-sqlite

wa-sqlite leverages WebAssembly (Wasm), allowing SQLite to run efficiently in web browser environments. The Wasm build is highly customizable by providing access to compiler options and having a good set of default options, however, it does not enable extension loading by default.

This Wasm build of SQLite supports writing to SQLite virtual file systems (VFS) completely in JavaScript and allows for alternative browser storage options such as IndexedDB and Origin Private File System (OPFS). Both the synchronous and asynchronous builds use the same set of default options and provide a consistent way to access the SQLite C API through JavaScript wrappers.

It does not provide an easy-to-use transaction API. It requires manual setup for concurrent read transactions. It does not include any update or commit hooks. Both builds allow the use of SQL queries directly, and support the use of prepared statements.

View library

React Native + op-sqlite

op-sqlite provides a low-level API for executing SQL queries within React Native. It provides access to compiler options configurable through the package.json file and caters for loading additional extensions.

It provides a consistent set of options across compatible platforms and direct access to the SQLite C API through JavaScript. It provides a comprehensive transaction API, exposes multiple parameter types and includes update hooks for various operations such as table or row updates. It also has support for prepared statements out of the box.

While it does not have the niceties of an ORM, it has a smaller footprint resulting in a smaller app size and better performance.

View library

Kotlin Multiplatform + SQLDelight

SQLDelight is one the few SQLite drivers that supports Kotlin Multiplatform and is used in almost all KMP samples and showcase apps. It does not expose many compiler options but does however include some good defaults that make extension loading possible. The library shows consistent performance across various platforms when compiling for KMP. It does not allow direct access to the SQLite C API which is provided by the SQLiter library under the hood. SQDelight does however have a rich SQLite API which generates type-safe Kotlin APIs from SQL which makes writing and maintaining SQL statements much easier. It does support querying the database asynchronously and allows the database to be configured for concurrent read transactions (using WAL mode).

View library

What is PowerSync?

PowerSync is a sync engine that keeps in-app SQLite databases in sync with backend databases.

PowerSync’s client SDKs bundle our recommended client-side SQLite libraries into an SDK which adds reactive live queries, queueing of uploads and network error handling on the client-side.

Learn more about PowerSync.
PowerSync SQLite architecture summary